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,68 @@
<?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-batik</artifactId>
<version>2.3-SNAPSHOT</version>
<name>TwelveMonkeys ImageIO Batik Plugin</name>
<description>
<![CDATA[
ImageIO wrapper for the Batik SVG Toolkit, enabling Scalable Vector Graphics (SVG) support.
See the <a href="http://xmlgraphics.apache.org/batik/">Batik Home page</a>
for more information.]]>
</description>
<parent>
<artifactId>twelvemonkeys-imageio</artifactId>
<groupId>com.twelvemonkeys</groupId>
<version>2.3-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-rasterizer-ext</artifactId>
<version>1.6-1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-svggen</artifactId>
<version>1.6-1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>batik</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.6-1</version>
<scope>provided</scope>
<!--
There seems to be some weirdness in the
Batik/FOP poms (Batik depends on FOP 0.20-5) that screws things up,
making everything end up depending on Batik 1.5, not 1.6
-->
<exclusions>
<exclusion>
<groupId>fop</groupId>
<artifactId>fop</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,574 @@
/*
* 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.plugins.svg;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import org.apache.batik.bridge.*;
import org.apache.batik.dom.svg.SVGDOMImplementation;
import org.apache.batik.dom.svg.SVGOMDocument;
import org.apache.batik.dom.util.DOMUtilities;
import org.apache.batik.ext.awt.image.GraphicsUtil;
import org.apache.batik.gvt.CanvasGraphicsNode;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.renderer.ConcreteImageRendererFactory;
import org.apache.batik.gvt.renderer.ImageRenderer;
import org.apache.batik.gvt.renderer.ImageRendererFactory;
import org.apache.batik.transcoder.*;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.util.ParsedURL;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGSVGElement;
import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
/**
* Image reader for SVG document fragments.
* <p/>
*
* @author Harald Kuhr
* @author Inpspired by code from the Batik Team
* @version $Id: $
* @see <A href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</A>
*/
public class SVGImageReader extends ImageReaderBase {
private Rasterizer mRasterizer = new Rasterizer();
/**
* Creates an {@code SVGImageReader}.
*
* @param pProvider the provider
*/
public SVGImageReader(ImageReaderSpi pProvider) {
super(pProvider);
}
protected void resetMembers() {
}
@Override
public void dispose() {
super.dispose();
mRasterizer = null;
}
@Override
public void setInput(Object pInput, boolean pSeekForwardOnly, boolean pIgnoreMetadata) {
super.setInput(pInput, pSeekForwardOnly, pIgnoreMetadata);
if (mImageInput != null) {
TranscoderInput input = new TranscoderInput(IIOUtil.createStreamAdapter(mImageInput));
mRasterizer.setInput(input);
}
}
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
checkBounds(pIndex);
String baseURI = null;
if (pParam instanceof SVGReadParam) {
SVGReadParam svgParam = (SVGReadParam) pParam;
// Set IIOParams as hints
// Note: The cast to Map invokes a different method that preserves
// unset defaults, DO NOT REMOVE!
mRasterizer.setTranscodingHints((Map) paramsToHints(svgParam));
// Get the base URI (not a hint)
baseURI = svgParam.getBaseURI();
}
Dimension size;
if (pParam != null && (size = pParam.getSourceRenderSize()) != null) {
// Use size...
}
else {
size = new Dimension(getWidth(pIndex), getHeight(pIndex));
}
BufferedImage destination = getDestination(pParam, getImageTypes(pIndex), size.width, size.height);
// Read in the image, using the Batik Transcoder
try {
processImageStarted(pIndex);
mRasterizer.mTranscoderInput.setURI(baseURI);
BufferedImage image = mRasterizer.getImage();
Graphics2D g = destination.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g.drawImage(image, 0, 0, null); // TODO: Dest offset?
}
finally {
g.dispose();
}
processImageComplete();
return destination;
}
catch (TranscoderException e) {
throw new IIOException(e.getMessage(), e);
}
}
private TranscodingHints paramsToHints(SVGReadParam pParam) throws IOException {
TranscodingHints hints = new TranscodingHints();
// Note: We must allow generic ImageReadParams, so converting to
// TanscodingHints should be done outside the SVGReadParam class.
// Set dimensions
Dimension size = pParam.getSourceRenderSize();
Dimension origSize = new Dimension(getWidth(0), getHeight(0));
if (size == null) {
// SVG is not a pixel based format, but we'll scale it, according to
// the subsampling for compatibility
size = getSourceRenderSizeFromSubsamping(pParam, origSize);
}
if (size != null) {
hints.put(ImageTranscoder.KEY_WIDTH, new Float(size.getWidth()));
hints.put(ImageTranscoder.KEY_HEIGHT, new Float(size.getHeight()));
}
// Set area of interest
Rectangle region = pParam.getSourceRegion();
if (region != null) {
hints.put(ImageTranscoder.KEY_AOI, region);
// Avoid that the batik transcoder scales the AOI up to original image size
if (size == null) {
hints.put(ImageTranscoder.KEY_WIDTH, new Float(region.getWidth()));
hints.put(ImageTranscoder.KEY_HEIGHT, new Float(region.getHeight()));
}
else {
// Need to resize here...
double xScale = size.getWidth() / origSize.getWidth();
double yScale = size.getHeight() / origSize.getHeight();
hints.put(ImageTranscoder.KEY_WIDTH, new Float(region.getWidth() * xScale));
hints.put(ImageTranscoder.KEY_HEIGHT, new Float(region.getHeight() * yScale));
}
}
else if (size != null) {
// Allow non-uniform scaling
hints.put(ImageTranscoder.KEY_AOI, new Rectangle(origSize));
}
// Background color
Paint bg = pParam.getBackgroundColor();
if (bg != null) {
hints.put(ImageTranscoder.KEY_BACKGROUND_COLOR, bg);
}
return hints;
}
private Dimension getSourceRenderSizeFromSubsamping(ImageReadParam pParam, Dimension pOrigSize) {
if (pParam.getSourceXSubsampling() > 1 || pParam.getSourceYSubsampling() > 1) {
return new Dimension((int) (pOrigSize.width / (float) pParam.getSourceXSubsampling()),
(int) (pOrigSize.height / (float) pParam.getSourceYSubsampling()));
}
return null;
}
public ImageReadParam getDefaultReadParam() {
return new SVGReadParam();
}
public int getWidth(int pIndex) throws IOException {
checkBounds(pIndex);
try {
return mRasterizer.getDefaultWidth();
}
catch (TranscoderException e) {
throw new IIOException(e.getMessage(), e);
}
}
public int getHeight(int pIndex) throws IOException {
checkBounds(pIndex);
try {
return mRasterizer.getDefaultHeight();
}
catch (TranscoderException e) {
throw new IIOException(e.getMessage(), e);
}
}
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
return Collections.singleton(ImageTypeSpecifier.createFromRenderedImage(mRasterizer.createImage(1, 1))).iterator();
}
/**
* An image transcoder that stores the resulting image.
* <p/>
* NOTE: This class includes a lot of copy and paste code from the Batik classes
* and needs major refactoring!
*/
private class Rasterizer extends SVGAbstractTranscoder /*ImageTranscoder*/ {
BufferedImage mImage = null;
private TranscoderInput mTranscoderInput;
private float mDefaultWidth;
private float mDefaultHeight;
private boolean mInit = false;
private SVGOMDocument mDocument;
private String mURI;
private GraphicsNode mGVTRoot;
private TranscoderException mException;
private BridgeContext mContext;
public BufferedImage createImage(int w, int h) {
return ImageUtil.createTransparent(w, h);//, BufferedImage.TYPE_INT_ARGB);
}
// This is cheating... We don't fully transcode after all
protected void transcode(Document document, String uri, TranscoderOutput output) throws TranscoderException {
// Sets up root, curTxf & curAoi
// ----
if ((document != null) &&
!(document.getImplementation() instanceof SVGDOMImplementation)) {
DOMImplementation impl;
impl = (DOMImplementation) hints.get(KEY_DOM_IMPLEMENTATION);
// impl = ExtensibleSVGDOMImplementation.getDOMImplementation();
document = DOMUtilities.deepCloneDocument(document, impl);
if (uri != null) {
try {
URL url = new URL(uri);
((SVGOMDocument) document).setURLObject(url);
}
catch (MalformedURLException ignore) {
}
}
}
ctx = createBridgeContext();
SVGOMDocument svgDoc = (SVGOMDocument) document;
//SVGSVGElement root = svgDoc.getRootElement();
// build the GVT tree
builder = new GVTBuilder();
// flag that indicates if the document is dynamic
boolean isDynamic =
(hints.containsKey(KEY_EXECUTE_ONLOAD) &&
(Boolean) hints.get(KEY_EXECUTE_ONLOAD) &&
BaseScriptingEnvironment.isDynamicDocument(ctx, svgDoc));
if (isDynamic) {
ctx.setDynamicState(BridgeContext.DYNAMIC);
}
// Modified code below:
GraphicsNode root = null;
try {
root = builder.build(ctx, svgDoc);
}
catch (BridgeException ex) {
// Note: This might fail, but we STILL have the dimensions we need
// However, we need to reparse later...
//throw new TranscoderException(ex);
mException = new TranscoderException(ex);
}
// ----
// get the 'width' and 'height' attributes of the SVG document
Dimension2D docSize = ctx.getDocumentSize();
if (docSize != null) {
mDefaultWidth = (float) docSize.getWidth();
mDefaultHeight = (float) docSize.getHeight();
}
else {
mDefaultWidth = 200;
mDefaultHeight = 200;
}
// Hack to work around exception above
if (root != null) {
mGVTRoot = root;
}
mDocument = svgDoc;
mURI = uri;
//ctx.dispose();
// Hack to avoid the transcode method wacking my context...
mContext = ctx;
ctx = null;
}
private BufferedImage readImage() throws TranscoderException {
init();
if (abortRequested()) {
processReadAborted();
return null;
}
processImageProgress(10f);
// Hacky workaround below...
if (mGVTRoot == null) {
// Try to reparse, if we had no URI last time...
if (mURI != mTranscoderInput.getURI()) {
try {
mContext.dispose();
mDocument.setURLObject(new URL(mTranscoderInput.getURI()));
transcode(mDocument, mTranscoderInput.getURI(), null);
}
catch (MalformedURLException ignore) {
// Ignored
}
}
if (mGVTRoot == null) {
throw mException;
}
}
ctx = mContext;
// /Hacky
if (abortRequested()) {
processReadAborted();
return null;
}
processImageProgress(20f);
// -- --
SVGSVGElement root = mDocument.getRootElement();
// ----
// ----
setImageSize(mDefaultWidth, mDefaultHeight);
if (abortRequested()) {
processReadAborted();
return null;
}
processImageProgress(40f);
// compute the preserveAspectRatio matrix
AffineTransform Px;
String ref = new ParsedURL(mURI).getRef();
try {
Px = ViewBox.getViewTransform(ref, root, width, height);
}
catch (BridgeException ex) {
throw new TranscoderException(ex);
}
if (Px.isIdentity() && (width != mDefaultWidth || height != mDefaultHeight)) {
// The document has no viewBox, we need to resize it by hand.
// we want to keep the document size ratio
float xscale, yscale;
xscale = width / mDefaultWidth;
yscale = height / mDefaultHeight;
float scale = Math.min(xscale, yscale);
Px = AffineTransform.getScaleInstance(scale, scale);
}
// take the AOI into account if any
if (hints.containsKey(KEY_AOI)) {
Rectangle2D aoi = (Rectangle2D) hints.get(KEY_AOI);
// transform the AOI into the image's coordinate system
aoi = Px.createTransformedShape(aoi).getBounds2D();
AffineTransform Mx = new AffineTransform();
double sx = width / aoi.getWidth();
double sy = height / aoi.getHeight();
Mx.scale(sx, sy);
double tx = -aoi.getX();
double ty = -aoi.getY();
Mx.translate(tx, ty);
// take the AOI transformation matrix into account
// we apply first the preserveAspectRatio matrix
Px.preConcatenate(Mx);
curAOI = aoi;
}
else {
curAOI = new Rectangle2D.Float(0, 0, width, height);
}
if (abortRequested()) {
processReadAborted();
return null;
}
processImageProgress(50f);
CanvasGraphicsNode cgn = getCanvasGraphicsNode(mGVTRoot);
if (cgn != null) {
cgn.setViewingTransform(Px);
curTxf = new AffineTransform();
}
else {
curTxf = Px;
}
try {
// dispatch an 'onload' event if needed
if (ctx.isDynamic()) {
BaseScriptingEnvironment se;
se = new BaseScriptingEnvironment(ctx);
se.loadScripts();
se.dispatchSVGLoadEvent();
}
}
catch (BridgeException ex) {
throw new TranscoderException(ex);
}
this.root = mGVTRoot;
// ----
// NOTE: The code below is copied and pasted from the Batik
// ImageTranscoder class' transcode() method:
// prepare the image to be painted
int w = (int) (width + 0.5);
int h = (int) (height + 0.5);
// paint the SVG document using the bridge package
// create the appropriate renderer
ImageRendererFactory rendFactory = new ConcreteImageRendererFactory();
// ImageRenderer renderer = rendFactory.createDynamicImageRenderer();
ImageRenderer renderer = rendFactory.createStaticImageRenderer();
renderer.updateOffScreen(w, h);
renderer.setTransform(curTxf);
renderer.setTree(this.root);
this.root = null; // We're done with it...
if (abortRequested()) {
processReadAborted();
return null;
}
processImageProgress(75f);
try {
// now we are sure that the aoi is the image size
Shape raoi = new Rectangle2D.Float(0, 0, width, height);
// Warning: the renderer's AOI must be in user space
renderer.repaint(curTxf.createInverse().
createTransformedShape(raoi));
// NOTE: repaint above cause nullpointer exception with fonts..???
BufferedImage rend = renderer.getOffScreen();
renderer = null; // We're done with it...
BufferedImage dest = createImage(w, h);
Graphics2D g2d = GraphicsUtil.createGraphics(dest);
try {
if (hints.containsKey(ImageTranscoder.KEY_BACKGROUND_COLOR)) {
Paint bgcolor = (Paint) hints.get(ImageTranscoder.KEY_BACKGROUND_COLOR);
g2d.setComposite(AlphaComposite.SrcOver);
g2d.setPaint(bgcolor);
g2d.fillRect(0, 0, w, h);
}
if (rend != null) { // might be null if the svg document is empty
g2d.drawRenderedImage(rend, new AffineTransform());
}
}
finally {
if (g2d != null) {
g2d.dispose();
}
}
if (abortRequested()) {
processReadAborted();
return null;
}
processImageProgress(99f);
return dest;
//writeImage(dest, output);
}
catch (Exception ex) {
throw new TranscoderException(ex.getMessage(), ex);
}
finally {
if (mContext != null) {
mContext.dispose();
}
}
}
private synchronized void init() throws TranscoderException {
if (!mInit) {
if (mTranscoderInput == null) {
throw new IllegalStateException("input == null");
}
mInit = true;
super.transcode(mTranscoderInput, null);
}
}
private BufferedImage getImage() throws TranscoderException {
if (mImage == null) {
mImage = readImage();
}
return mImage;
}
protected int getDefaultWidth() throws TranscoderException {
init();
return (int) (mDefaultWidth + 0.5);
}
protected int getDefaultHeight() throws TranscoderException {
init();
return (int) (mDefaultHeight + 0.5);
}
public void setInput(TranscoderInput pInput) {
mTranscoderInput = pInput;
}
}
}

View File

@@ -0,0 +1,174 @@
/*
* 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.plugins.svg;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.lang.SystemUtil;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* SVGImageReaderSpi
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: SVGImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 haku Exp $
*/
public class SVGImageReaderSpi extends ImageReaderSpi {
private final static boolean SVG_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader");
/**
* Creates an {@code SVGImageReaderSpi}.
*/
public SVGImageReaderSpi() {
this(IIOUtil.getProviderInfo(SVGImageReaderSpi.class));
}
private SVGImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(), // Vendor name
pProviderInfo.getVersion(), // Version
SVG_READER_AVAILABLE ? new String[]{"svg", "SVG"} : new String[]{""}, // Names
SVG_READER_AVAILABLE ? new String[]{"svg"} : null, // Suffixes
SVG_READER_AVAILABLE ? new String[]{"image/svg", "image/x-svg", "image/svg+xml", "image/svg-xml"} : null, // Mime-types
"com.twelvemonkeys.imageio.plugins.svg.SVGImageReader", // Reader class name
ImageReaderSpi.STANDARD_INPUT_TYPE, // Output types
null, // Writer SPI names
true, // Supports standard stream metadata format
null, // Native stream metadata format name
null, // Native stream metadata format class name
null, // Extra stream metadata format names
null, // Extra stream metadata format class names
true, // Supports standard image metadata format
null, // Native image metadata format name
null, // Native image metadata format class name
null, // Extra image metadata format names
null // Extra image metadata format class names
);
}
public boolean canDecodeInput(Object pSource) throws IOException {
return pSource instanceof ImageInputStream && SVG_READER_AVAILABLE && canDecode((ImageInputStream) pSource);
}
private static boolean canDecode(ImageInputStream pInput) throws IOException {
// NOTE: This test is quite quick as it does not involve any parsing,
// however it requires the doctype to be "svg", which may not be correct
// in all cases...
try {
pInput.mark();
// TODO: This is may not be ok for non-UTF/iso-latin encodings...
// TODO: Use an XML (encoding) aware Reader instance instead
// Need to figure out pretty fast if this is XML or not
int b;
while (Character.isWhitespace((char) (b = pInput.read()))) {
// Skip over leading WS
}
if (!((b == '<') && (pInput.read() == '?') && (pInput.read() == 'x') && (pInput.read() == 'm')
&& (pInput.read() == 'l'))) {
return false;
}
// Okay, we have XML. But, is it really SVG?
boolean docTypeFound = false;
while (!docTypeFound) {
while (pInput.read() != '<') {
// Skip over, until begin tag
}
// If this is not a comment, or the DOCTYPE declaration, the doc
// has no DOCTYPE and it can't be svg
if (pInput.read() != '!') {
return false;
}
// There might be comments before the doctype, unfortunately...
// If next is "--", this is a comment
if ((b = pInput.read()) == '-' && pInput.read() == '-') {
while (!(pInput.read() == '-' && pInput.read() == '-' && pInput.read() == '>')) {
// Skip until end of comment
}
}
// If we are lucky, this is DOCTYPE declaration
if (b == 'D' && pInput.read() == 'O' && pInput.read() == 'C'
&& pInput.read() == 'T' && pInput.read() == 'Y' && pInput.read() == 'P'
&& pInput.read() == 'E') {
docTypeFound = true;
while (Character.isWhitespace((char) (b = pInput.read()))) {
// Skip over WS
}
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') {
//System.out.println("It's svg!");
return true;
}
}
}
return false;
}
finally {
pInput.reset();
}
}
public ImageReader createReaderInstance(Object extension) throws IOException {
return new SVGImageReader(this);
}
public String getDescription(Locale locale) {
return "Scaleable Vector Graphics (SVG) format image reader";
}
@SuppressWarnings({"deprecation"})
@Override
public void onRegistration(ServiceRegistry registry, Class<?> category) {
if (!SVG_READER_AVAILABLE) {
try {
// NOTE: This will break, but it gives us some useful debug info
new SVGImageReader(this);
}
catch (Throwable t) {
System.err.println("Could not instantiate SVGImageReader (missing support classes).");
t.printStackTrace();
}
IIOUtil.deregisterProvider(registry, this, category);
}
}}

View File

@@ -0,0 +1,63 @@
/*
* 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.plugins.svg;
import javax.imageio.ImageReadParam;
import java.awt.*;
/**
* Implementation of {@code IamgeReadParam} for SVG images.
* SVG images allows for different source render sizes.
*
*/
public class SVGReadParam extends ImageReadParam {
private Paint mBackground;
private String mBaseURI;
public Paint getBackgroundColor() {
return mBackground;
}
public void setBackgroundColor(Paint pColor) {
mBackground = pColor;
}
public String getBaseURI() {
return mBaseURI;
}
public void setBaseURI(String pBaseURI) {
mBaseURI = pBaseURI;
}
@Override
public boolean canSetSourceRenderSize() {
return true;
}
}

View File

@@ -0,0 +1,189 @@
/*
* 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.plugins.tiff;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import org.apache.batik.ext.awt.image.codec.SeekableStream;
import org.apache.batik.ext.awt.image.codec.tiff.TIFFDecodeParam;
import org.apache.batik.ext.awt.image.codec.tiff.TIFFImageDecoder;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* TIFFImageReader class description.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: TIFFImageReader.java,v 1.0 29.jul.2004 12:52:33 haku Exp $
*/
// TODO: Massive clean-up
// TODO: Support raster decoding...
public class TIFFImageReader extends ImageReaderBase {
private TIFFImageDecoder mDecoder = null;
private List<RenderedImage> mImages = new ArrayList<RenderedImage>();
protected TIFFImageReader(final ImageReaderSpi pOriginatingProvider) {
super(pOriginatingProvider);
}
protected void resetMembers() {
mDecoder = null;
}
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
// Decode image, convert and return as BufferedImage
RenderedImage image = readAsRenderedImage(pIndex, pParam);
return ImageUtil.toBuffered(image);
}
public RenderedImage readAsRenderedImage(int pIndex, ImageReadParam pParam) throws IOException {
init(pIndex);
processImageStarted(pIndex);
if (pParam == null) {
// Cache image for use by getWidth and getHeight methods
RenderedImage image;
if (mImages.size() > pIndex && mImages.get(pIndex) != null) {
image = mImages.get(pIndex);
}
else {
// Decode
image = mDecoder.decodeAsRenderedImage(pIndex);
// Make room
for (int i = mImages.size(); i < pIndex; i++) {
mImages.add(pIndex, null);
}
mImages.add(pIndex, image);
}
if (abortRequested()) {
processReadAborted();
return image;
}
processImageComplete();
return image;
}
else {
// TODO: Parameter conversion
mDecoder.setParam(new TIFFDecodeParam());
RenderedImage image = mDecoder.decodeAsRenderedImage(pIndex);
// Subsample and apply AOI
if (pParam.getSourceRegion() != null) {
image = fakeAOI(ImageUtil.toBuffered(image), pParam);
}
if (pParam.getSourceXSubsampling() > 1 || pParam.getSourceYSubsampling() > 1) {
image = ImageUtil.toBuffered(fakeSubsampling(ImageUtil.toBuffered(image), pParam));
}
processImageComplete();
return image;
}
}
private void init(int pIndex) throws IOException {
init();
checkBounds(pIndex);
}
protected void checkBounds(int pIndex) throws IOException {
if (pIndex < getMinIndex()){
throw new IndexOutOfBoundsException("index < minIndex");
}
else if (pIndex >= getNumImages(true)) {
throw new IndexOutOfBoundsException("index > numImages");
}
}
private synchronized void init() {
if (mDecoder == null) {
if (mImageInput == null) {
throw new IllegalStateException("input == null");
}
mDecoder = new TIFFImageDecoder(new SeekableStream() {
public int read() throws IOException {
return mImageInput.read();
}
public int read(final byte[] pBytes, final int pStart, final int pLength) throws IOException {
return mImageInput.read(pBytes, pStart, pLength);
}
public long getFilePointer() throws IOException {
return mImageInput.getStreamPosition();
}
public void seek(final long pPos) throws IOException {
mImageInput.seek(pPos);
}
}, null);
}
}
public int getWidth(int pIndex) throws IOException {
init(pIndex);
// TODO: Use cache...
return mDecoder.decodeAsRenderedImage(pIndex).getWidth();
}
public int getHeight(int pIndex) throws IOException {
init(pIndex);
// TODO: Use cache...
return mDecoder.decodeAsRenderedImage(pIndex).getHeight();
}
public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
throw new UnsupportedOperationException("Method getImageTypes not implemented");// TODO: Implement
}
public int getNumImages(boolean pAllowSearch) throws IOException {
init();
if (pAllowSearch) {
return mDecoder.getNumPages();
}
return -1;
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.plugins.tiff;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.lang.SystemUtil;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* TIFFImageReaderSpi
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: TIFFImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $
*/
public class TIFFImageReaderSpi extends ImageReaderSpi {
final static boolean TIFF_CLASSES_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader");
/**
* Creates a {@code TIFFImageReaderSpi}.
*/
public TIFFImageReaderSpi() {
this(IIOUtil.getProviderInfo(TIFFImageReaderSpi.class));
}
private TIFFImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(), // Vendor name
pProviderInfo.getVersion(), // Version
TIFF_CLASSES_AVAILABLE ? new String[]{"tiff", "TIFF"} : new String[] {""}, // Names
TIFF_CLASSES_AVAILABLE ? new String[]{"tiff", "tif"} : null, // Suffixes
TIFF_CLASSES_AVAILABLE ? new String[]{"image/tiff", "image/x-tiff"} : null, // Mime-types
"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReader", // Writer class name..?
ImageReaderSpi.STANDARD_INPUT_TYPE, // Output types
new String[]{"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi"}, // Writer SPI names
true, // Supports standard stream metadata format
null, // Native stream metadata format name
null, // Native stream metadata format class name
null, // Extra stream metadata format names
null, // Extra stream metadata format class names
true, // Supports standard image metadata format
null, // Native image metadata format name
null, // Native image metadata format class name
null, // Extra image metadata format names
null // Extra image metadata format class names
);
}
public boolean canDecodeInput(Object source) throws IOException {
return source instanceof ImageInputStream && TIFF_CLASSES_AVAILABLE && canDecode((ImageInputStream) source);
}
static boolean canDecode(ImageInputStream pInput) throws IOException {
try {
pInput.mark();
int byte0 = pInput.read(); // Byte order 1 (M or I)
int byte1 = pInput.read(); // Byte order 2 (always same as 1)
int byte2 = pInput.read(); // Version number 1 (M: 0, I: 42)
int byte3 = pInput.read(); // Version number 2 (M: 42, I: 0)
// Test for Motorola or Intel byte order, and version number == 42
if ((byte0 == 'M' && byte1 == 'M' && byte2 == 0 && byte3 == 42)
|| (byte0 == 'I' && byte1 == 'I' && byte2 == 42 && byte3 == 0)) {
return true;
}
}
finally {
pInput.reset();
}
return false;
}
public ImageReader createReaderInstance(Object extension) throws IOException {
return new TIFFImageReader(this);
}
public String getDescription(Locale locale) {
return "Tagged Image File Format (TIFF) image reader";
}
@SuppressWarnings({"deprecation"})
@Override
public void onRegistration(ServiceRegistry registry, Class<?> category) {
if (!TIFF_CLASSES_AVAILABLE) {
IIOUtil.deregisterProvider(registry, this, category);
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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.plugins.tiff;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageWriterBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import org.apache.batik.ext.awt.image.codec.ImageEncodeParam;
import org.apache.batik.ext.awt.image.codec.tiff.TIFFEncodeParam;
import org.apache.batik.ext.awt.image.codec.tiff.TIFFImageEncoder;
import javax.imageio.IIOImage;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
/**
* TIFFImageWriter class description.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: TIFFImageWriter.java,v 1.0 29.jul.2004 12:52:54 haku Exp $
*/
public class TIFFImageWriter extends ImageWriterBase {
private TIFFImageEncoder mEncoder = null;
protected TIFFImageWriter(final ImageWriterSpi pProvider) {
super(pProvider);
}
@Override
public void setOutput(final Object pOutput) {
mEncoder = null;
super.setOutput(pOutput);
}
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
throw new UnsupportedOperationException("Method getDefaultImageMetadata not implemented");// TODO: Implement
}
public IIOMetadata convertImageMetadata(final IIOMetadata inData, final ImageTypeSpecifier imageType, final ImageWriteParam param) {
throw new UnsupportedOperationException("Method convertImageMetadata not implemented");// TODO: Implement
}
public void write(final IIOMetadata pStreamMetadata, final IIOImage pImage, final ImageWriteParam pParam) throws IOException {
RenderedImage renderedImage = pImage.getRenderedImage();
init();
ImageEncodeParam param;
if (pParam != null) {
param = new TIFFEncodeParam();
// TODO: Convert params
mEncoder.setParam(param);
}
BufferedImage image;
// FIX: TIFFEnocder chokes on a any of the TYPE_INT_* types...
// (The TIFFEncoder expects int types to have 1 sample of size 32
// while there actually is 4 samples of size 8, according to the
// SampleModel...)
if (renderedImage instanceof BufferedImage && (
((BufferedImage) renderedImage).getType() == BufferedImage.TYPE_INT_ARGB
|| ((BufferedImage) renderedImage).getType() == BufferedImage.TYPE_INT_ARGB_PRE)) {
image = ImageUtil.toBuffered(renderedImage, BufferedImage.TYPE_4BYTE_ABGR);
}
else if (renderedImage instanceof BufferedImage && (
((BufferedImage) renderedImage).getType() == BufferedImage.TYPE_INT_BGR
|| ((BufferedImage) renderedImage).getType() == BufferedImage.TYPE_INT_RGB)) {
image = ImageUtil.toBuffered(renderedImage, BufferedImage.TYPE_3BYTE_BGR);
}
else {
image = ImageUtil.toBuffered(renderedImage);
}
image = fakeAOI(image, pParam);
image = ImageUtil.toBuffered(fakeSubsampling(image, pParam));
/*
System.out.println("Image: " + pImage);
SampleModel sampleModel = pImage.getSampleModel();
System.out.println("SampleModel: " + sampleModel);
int sampleSize[] = sampleModel.getSampleSize();
System.out.println("Samples: " + sampleSize.length);
for (int i = 0; i < sampleSize.length; i++) {
System.out.println("SampleSize[" + i + "]: " + sampleSize[i]);
}
int dataType = sampleModel.getDataType();
System.out.println("DataType: " + dataType);
*/
processImageStarted(0);
mEncoder.encode(image);
mImageOutput.flush();
processImageComplete();
}
public void dispose() {
super.dispose();
mEncoder = null;
}
private synchronized void init() {
if (mEncoder == null) {
if (mImageOutput == null) {
throw new IllegalStateException("output == null");
}
mEncoder = new TIFFImageEncoder(IIOUtil.createStreamAdapter(mImageOutput), null);
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.plugins.tiff;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.spi.ServiceRegistry;
import java.io.IOException;
import java.util.Locale;
/**
* TIFFmageWriterSpi
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: TIFFImageWriterSpi.java,v 1.2 2004/01/14 15:21:44 wmhakur Exp $
*/
public class TIFFImageWriterSpi extends ImageWriterSpi {
/**
* Creates a {@code TIFFImageWriterSpi}.
*/
public TIFFImageWriterSpi() {
this(IIOUtil.getProviderInfo(TIFFImageWriterSpi.class));
}
private TIFFImageWriterSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(), // Vendor name
pProviderInfo.getVersion(), // Version
new String[]{"tiff", "TIFF"}, // Names
new String[]{"tif", "tiff"}, // Suffixes
new String[]{"image/tiff", "image/x-tiff"}, // Mime-types
"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriter", // Writer class name..?
STANDARD_OUTPUT_TYPE, // Output types
new String[]{"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi"}, // Reader SPI names
true, // Supports standard stream metadata format
null, // Native stream metadata format name
null, // Native stream metadata format class name
null, // Extra stream metadata format names
null, // Extra stream metadata format class names
true, // Supports standard image metadata format
null, // Native image metadata format name
null, // Native image metadata format class name
null, // Extra image metadata format names
null // Extra image metadata format class names
);
}
public boolean canEncodeImage(ImageTypeSpecifier type) {
return true;
}
public ImageWriter createWriterInstance(Object extension) throws IOException {
try {
return new TIFFImageWriter(this);
}
catch (Throwable t) {
// Wrap in IOException if the writer can't be instantiated.
// This makes the IIORegistry deregister this service provider
IOException exception = new IOException(t.getMessage());
exception.initCause(t);
throw exception;
}
}
public String getDescription(Locale locale) {
return "Tagged Image File Format (TIFF) image writer";
}
@SuppressWarnings({"deprecation"})
@Override
public void onRegistration(ServiceRegistry registry, Class<?> category) {
if (!TIFFImageReaderSpi.TIFF_CLASSES_AVAILABLE) {
IIOUtil.deregisterProvider(registry, this, category);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.plugins.wmf;
/**
* WMF
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: WMF.java,v 1.0 Feb 17, 2008 5:46:59 PM haraldk Exp$
*/
interface WMF {
static byte[] HEADER = new byte[] {
(byte) 0xd7, (byte) 0xcd, (byte) 0xc6, (byte) 0x9a, (byte) 0x00,
(byte) 0x00, //(byte) 0x7a, (byte) 0xf3, (byte) 0xa6, (byte) 0xfe,
//(byte) 0xf5, (byte) 0x06, (byte) 0x1c, (byte) 0x01, (byte) 0xe8,
//(byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
//(byte) 0xcc,
};
}

View File

@@ -0,0 +1,137 @@
/*
* 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.plugins.wmf;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.plugins.svg.SVGImageReader;
import com.twelvemonkeys.imageio.plugins.svg.SVGReadParam;
import com.twelvemonkeys.imageio.util.IIOUtil;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Iterator;
/**
* WMFImageReader class description.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: WMFImageReader.java,v 1.0 29.jul.2004 13:00:59 haku Exp $
*/
// TODO: Probably possible to do less wrapping/unwrapping of data...
// TODO: Consider using temp file instead of in-memory stream
public class WMFImageReader extends ImageReaderBase {
private SVGImageReader mReader = null;
public WMFImageReader(final ImageReaderSpi pProvider) {
super(pProvider);
}
protected void resetMembers() {
if (mReader != null) {
mReader.dispose();
}
mReader = null;
}
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
init();
processImageStarted(pIndex);
BufferedImage image = mReader.read(pIndex, pParam);
if (abortRequested()) {
processReadAborted();
return image;
}
processImageComplete();
return image;
}
private synchronized void init() throws IOException {
// Need the extra test, to avoid throwing an IOException from the Transcoder
if (mImageInput == null) {
throw new IllegalStateException("input == null");
}
if (mReader == null) {
WMFTranscoder transcoder = new WMFTranscoder();
ByteArrayOutputStream output = new ByteArrayOutputStream();
Writer writer = new OutputStreamWriter(output, "UTF8");
try {
TranscoderInput in = new TranscoderInput(IIOUtil.createStreamAdapter(mImageInput));
TranscoderOutput out = new TranscoderOutput(writer);
// TODO: Transcodinghints?
transcoder.transcode(in, out);
}
catch (TranscoderException e) {
throw new IIOException(e.getMessage(), e);
}
mReader = new SVGImageReader(getOriginatingProvider());
mReader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(output.toByteArray())));
}
}
@Override
public ImageReadParam getDefaultReadParam() {
return new SVGReadParam();
}
public int getWidth(int pIndex) throws IOException {
init();
return mReader.getWidth(pIndex);
}
public int getHeight(int pIndex) throws IOException {
init();
return mReader.getHeight(pIndex);
}
public Iterator<ImageTypeSpecifier> getImageTypes(final int pImageIndex) throws IOException {
init();
return mReader.getImageTypes(pImageIndex);
}
}

View File

@@ -0,0 +1,128 @@
/*
* 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.plugins.wmf;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.lang.SystemUtil;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* WMFImageReaderSpi
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: WMFImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $
*/
public class WMFImageReaderSpi extends ImageReaderSpi {
// This is correct, as we rely on the SVG reader
private final static boolean WMF_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader");
/**
* Creates a {@code WMFImageReaderSpi}.
*/
public WMFImageReaderSpi() {
this(IIOUtil.getProviderInfo(WMFImageReaderSpi.class));
}
private WMFImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(), // Vendor name
pProviderInfo.getVersion(), // Version
WMF_READER_AVAILABLE ? new String[]{"wmf", "WMF"} : new String[]{""}, // Names
WMF_READER_AVAILABLE ? new String[]{"wmf", "emf"} : null, // Suffixes
WMF_READER_AVAILABLE ? new String[]{"application/x-msmetafile", "image/x-wmf"} : null, // Mime-types
WMFImageReader.class.getName(), // Reader class name..?
ImageReaderSpi.STANDARD_INPUT_TYPE, // Output types
null, // Writer SPI names
true, // Supports standard stream metadata format
null, // Native stream metadata format name
null, // Native stream metadata format class name
null, // Extra stream metadata format names
null, // Extra stream metadata format class names
true, // Supports standard image metadata format
null, // Native image metadata format name
null, // Native image metadata format class name
null, // Extra image metadata format names
null // Extra image metadata format class names
);
}
public boolean canDecodeInput(Object source) throws IOException {
return source instanceof ImageInputStream && WMF_READER_AVAILABLE && canDecode((ImageInputStream) source);
}
public static boolean canDecode(ImageInputStream pInput) throws IOException {
if (pInput == null) {
throw new IllegalArgumentException("input == null");
}
try {
pInput.mark();
for (byte header : WMF.HEADER) {
int read = (byte) pInput.read();
if (header != read) {
// System.out.println("--> " + i + ": " + read + " (expected " + header + ")");
return false;
}
}
return true;
}
finally {
pInput.reset();
}
}
public ImageReader createReaderInstance(Object extension) throws IOException {
return new WMFImageReader(this);
}
public String getDescription(Locale locale) {
return "Windows Meta File (WMF) image reader";
}
@SuppressWarnings({"deprecation"})
@Override
public void onRegistration(ServiceRegistry registry, Class<?> category) {
if (!WMF_READER_AVAILABLE) {
IIOUtil.deregisterProvider(registry, this, category);
}
}
}

View File

@@ -0,0 +1,3 @@
com.twelvemonkeys.imageio.plugins.svg.SVGImageReaderSpi
com.twelvemonkeys.imageio.plugins.wmf.WMFImageReaderSpi
#com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi

View File

@@ -0,0 +1 @@
#com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi

View File

@@ -0,0 +1,78 @@
/*
* 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.plugins.svg;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/**
* SVGImageReaderTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: SVGImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class SVGImageReaderTestCase extends ImageReaderAbstractTestCase<SVGImageReader> {
private SVGImageReaderSpi mSVGImageReaderSpi = new SVGImageReaderSpi();
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/svg/batikLogo.svg"), new Dimension(450, 500))
);
}
protected ImageReaderSpi createProvider() {
return mSVGImageReaderSpi;
}
@Override
protected SVGImageReader createReader() {
return new SVGImageReader(createProvider());
}
protected Class<SVGImageReader> getReaderClass() {
return SVGImageReader.class;
}
protected List<String> getFormatNames() {
return Arrays.asList("svg");
}
protected List<String> getSuffixes() {
return Arrays.asList("svg");
}
protected List<String> getMIMETypes() {
return Arrays.asList("image/svg+xml");
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.plugins.wmf;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/**
* SVGImageReaderTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: SVGImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class WMFImageReaderTestCase extends ImageReaderAbstractTestCase<WMFImageReader> {
private WMFImageReaderSpi mSVGImageReaderSpi = new WMFImageReaderSpi();
protected List<TestData> getTestData() {
return Arrays.asList(
// TODO: Dimensions does not look right...
new TestData(getClassLoaderResource("/wmf/test.wmf"), new Dimension(841, 673))
);
}
protected ImageReaderSpi createProvider() {
return mSVGImageReaderSpi;
}
@Override
protected WMFImageReader createReader() {
return new WMFImageReader(createProvider());
}
protected Class<WMFImageReader> getReaderClass() {
return WMFImageReader.class;
}
protected List<String> getFormatNames() {
return Arrays.asList("wmf");
}
protected List<String> getSuffixes() {
return Arrays.asList("wmf", "emf");
}
protected List<String> getMIMETypes() {
return Arrays.asList("image/x-wmf", "application/x-msmetafile");
}
}

View File

@@ -0,0 +1,218 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!--
============================================================================
The Apache Software License, Version 1.1
============================================================================
Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. 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.
3. The end-user documentation included with the redistribution, if any, must
include the following acknowledgment: "This product includes software
developed by the Apache Software Foundation (http://www.apache.org/)."
Alternately, this acknowledgment may appear in the software itself, if
and wherever such third-party acknowledgments normally appear.
4. The names "Batik" and "Apache Software Foundation" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
apache@apache.org.
5. Products derived from this software may not be called "Apache", nor may
"Apache" appear in their name, without prior written permission of the
Apache Software Foundation.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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
APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
DING, 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.
This software consists of voluntary contributions made by many individuals
on behalf of the Apache Software Foundation. For more information on the
Apache Software Foundation, please see <http://www.apache.org/>.
-->
<!-- ====================================================================== -->
<!-- Defines the Batik Logo using an SVG font. -->
<!-- -->
<!-- @author vhardy@apache.org -->
<!-- @author thomas.deweese@kodak.com -->
<!-- @author bella.robinson@cmis.csiro.au -->
<!-- @version $Id: batikLogo.svg,v 1.13 2003/08/08 11:39:29 vhardy Exp $ -->
<!-- ====================================================================== -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body" width="450" height="500" viewBox="0 0 450 500">
<title>Batik Logo</title>
<g id="content">
<defs>
<symbol id="Batik_Squiggle" stroke="none" viewBox="0 0 540 570">
<path id="Batik_Squiggle_Blue" fill="#6666FF"
d="M172,44C137,60,31,135,11,199c-8,27,22,48,44,33
C14,306-1,332,0,356c0,14,13,42,44,27c8-4,35-25,52-41
c14-1,24-11,42-28c17,14,36,10,52-7c22,2,82-78,44-108
c-3-24-30-37-53-18c-6-2-13-1-18,1c22-35,43-82,49-105
C219,47,188,36,172,44z"/>
<path id="Batik_Squiggle_Red" fill="#FF0000"
d="M400,0c-18,3-49,31-49,31c-29,23-43,58-28,95
c-13,14-29,44-29,67c0,28,20,52,50,29c7,8,21,16,37,5
c-5,29,3,48,26,49c1,10,13,31,36,17c16-10,58-39,79-56
c25-23,25-94-18-89c33-59-3-96-27-84c-10,4-46,25-52,30
c-1-7-5-12-11-14C436,45,436-5,401,0z"/>
<path id="Batik_Squiggle_Green" fill="#33CC33"
d="M275,353c-46,12-88,43-114,91c-9,16,6,37,25,33
c-14,24-40,67-15,81c28,16,52-8,60-15c18,21,50,10,81-17
c41,14,68-2,103-53c8-12,30-43,30-65c0-16-15-30-35-21
c-1-12-9-38-53-19c-10-6-31-5-54,17
C308,375,300,347,275,353z"/>
</symbol>
<!-- ============================= -->
<!-- Batik SVG Font Definition -->
<!-- ============================= -->
<font horiz-adv-x="150" id="Batik">
<font-face
font-family="Batik SVGFont"
units-per-em="240"
ascent="190"
descent="50"
alphabetic="0"/>
<missing-glyph horiz-adv-x="150" d="M20 0 V240 H100 V0 z"/>
<glyph unicode=" " glyph-name=" " horiz-adv-x="100"/>
<glyph id="B" unicode="B" glyph-name="B" horiz-adv-x="130">
<g transform="scale(1,-1)translate(0,-170)">
<path d="M21.244,141.963V40.831c0-6.188-0.57-10.773-1.707-13.754c-1.137-2.977-3.066-5.461-5.793-7.449c-1.137-0.766-2.367-1.395-3.695-1.891s-3.012-0.938-5.055-1.32c-2.125-0.371-3.488-0.781-4.094-1.23s-0.906-1.121-0.906-2.02
c0-1.195,0.32-2.035,0.969-2.52c0.645-0.484,1.953-0.73,3.93-0.73c0.758,0,3.816,0.211,9.176,0.625c5.355,0.418,10.387,0.625,15.098,0.625c2.961,0,7.883-0.207,14.758-0.625c6.875-0.414,12.324-0.625,16.352-0.625c16.711,0,29.762,3.461,39.145,10.379
s14.074,16.574,14.074,28.965c0,7.148-1.793,13.418-5.375,18.816c-3.586,5.398-9,9.996-16.242,13.797v2.18c11.574,2.051,20.445,6.547,26.613,13.492s9.254,15.879,9.254,26.805c0,15.406-5.184,27.645-15.551,36.715s-24.473,13.602-42.316,13.602
c-6.078,0-13.367-0.293-21.871-0.875c-8.508-0.586-13.898-0.875-16.172-0.875c-6.762,0-13.863,0.348-21.301,1.043c-1.824,0.137-2.965,0.207-3.418,0.207c-0.609,0-1.199-0.344-1.77-1.027s-0.852-1.406-0.852-2.172c0-1.598,1.355-2.93,4.074-3.996l0.113-0.055
c1.809-0.836,3.223-1.574,4.242-2.223c1.02-0.645,1.906-1.387,2.66-2.223c2.039-2.047,3.492-4.516,4.359-7.402s1.301-7.254,1.301-13.105z M39.244,73.209c0,3.648,0.453,5.93,1.367,6.84c0.914,0.914,2.816,1.367,5.711,1.367h16.555
c12.023,0,20.758-2.031,26.203-6.098c5.441-4.066,8.164-10.508,8.164-19.324c0-10.945-4.188-20.027-12.559-27.246c-8.375-7.219-18.914-10.832-31.625-10.832c-5.711,0-9.441,0.855-11.191,2.566s-2.625,5.148-2.625,10.316v42.41z M39.244,150.737
c0,6.539,1.789,10.953,5.371,13.242c3.578,2.293,11.16,3.438,22.746,3.438c14.172,0,24.82-3.031,31.945-9.094s10.688-15.156,10.688-27.281c0-13.031-4.234-23.188-12.695-30.461s-20.316-10.914-35.563-10.914H47.463c-3.578,0-5.84,0.477-6.793,1.426
s-1.426,3.285-1.426,7.004v52.641z"/>
<!-- Put the Squiggle in the B -->
<use xlink:href="#Batik_Squiggle" width="54" height="57"
transform="translate(45,103)" />
</g>
</glyph>
<glyph id="a" unicode="a" glyph-name="a" horiz-adv-x="105">
<path transform="scale(1,-1)translate(-125, -170)"
d="M194.825,161.952c-5.238,4.766-10.891,8.285-16.961,10.559c-6.07,2.27-12.863,3.406-20.375,3.406c-7.363,0-12.98-1.922-16.848-5.762c-3.871-3.844-5.805-9.414-5.805-16.719c0-9.359,4.266-16.758,12.805-22.195
c8.535-5.438,23.766-10.215,45.695-14.324v-15.789c0-7.09-2.16-12.523-6.477-16.297s-10.523-5.664-18.625-5.664c-6.891,0-11.758,0.992-14.598,2.977s-4.258,5.336-4.258,10.063c0,1.984,0.281,4.27,0.852,6.863s0.855,4.156,0.855,4.688
c0,1.07-0.516,1.945-1.547,2.633s-2.352,1.027-3.953,1.027c-3.055,0-5.652-0.816-7.793-2.449s-3.207-3.664-3.207-6.098c0-6.605,3.664-12.625,11-18.055c7.332-5.43,15.977-8.148,25.93-8.148c13.906,0,23.727,2.621,29.465,7.855
c5.734,5.238,8.605,14.535,8.605,27.891v42.844c0,6.516,0.621,10.715,1.867,12.594s3.609,2.816,7.086,2.816c0.602,0,1.434-0.035,2.492-0.113c1.055-0.078,1.773-0.117,2.152-0.117c0.527,0,1.02,0.246,1.473,0.73c0.453,0.488,0.68,1.07,0.68,1.742
c0,1.574-1.273,2.887-3.816,3.934s-5.785,1.574-9.73,1.574c-4.176,0-7.668-1.039-10.477-3.117s-4.973-5.191-6.488-9.348z M193.037,122.167c-16.43,3.43-27.789,7.273-34.074,11.535c-6.285,4.266-9.426,9.973-9.426,17.129c0,5.559,1.512,9.879,4.543,12.961
c3.027,3.086,7.27,4.625,12.723,4.625c7.492,0,13.738-1.941,18.738-5.832c4.996-3.887,7.496-8.813,7.496-14.777v-25.641z"/>
</glyph>
<glyph id="ti" unicode="ti" glyph-name="ti" horiz-adv-x="100">
<g style="fill:#FF0000;" transform="scale(1,-1)translate(-215,-170)">
<path d="M311.259,168.69c-0.684-0.531-2.199-0.871-4.551-1.023c-1.441,0-2.711-0.113-3.813-0.34s-2.105-0.57-3.012-1.027c-3.035-1.594-5.102-3.586-6.203-5.98c-1.102-2.391-1.648-6.625-1.648-12.703v-35.543c0-11.688,0.188-23.227,0.566-34.617
c0.078-2.047,0.117-3.227,0.117-3.531c0-1.594-0.191-2.617-0.57-3.074c-0.383-0.453-1.066-0.684-2.059-0.684c-1.066,0-9.44,3.681-11.451,4.196s-6.655,1.804-11.209,1.804h-20.266V55.045c0-1.148-0.117-1.918-0.344-2.301s-0.684-0.578-1.363-0.578
c-1.219,0-3.059,2.172-5.527,6.516s-4.727,7.617-6.777,9.824c-2.887,3.199-5.98,6.246-9.285,9.141s-4.953,4.609-4.953,5.141c0,0.609,0.375,1.203,1.129,1.773s1.434,0.855,2.035,0.855h8.586v59.84c0,11.266,2.051,19.273,6.16,24.027
c4.105,4.754,10.875,7.133,20.305,7.133c5.724,0,11.038-1.066,15.948-3.17c4.26-0.381,8.633-0.58,13.126-0.58c4.328,0,8.957,0.211,13.895,0.625c4.934,0.414,7.668,0.625,8.199,0.625c1.141,0,2.09-0.266,2.848-0.793c0.758-0.531,1.141-1.176,1.141-1.934
c0-1.137-0.344-1.969-1.023-2.5z M251.317,163.288c-2.773-2.922-4.156-7.227-4.156-12.914v-64.957c0,0,12.812,0.543,13.215,0.57c1.194,0.081,2.965,0.184,5.164,0.184c3.867,0,6.23,1.637,7.637,3.914c1.402,2.281,2.105,7.367,2.105,15.266v42.039
c0,4.781-0.285,8.273-0.848,10.477c-0.566,2.203-1.563,4.211-2.992,6.031c-0.758,0.836-1.961,1.863-3.617,3.074c-0.292,0.169-0.532,0.312-0.731,0.434c-1.229,0.172-2.446,0.261-3.651,0.261c-5.313,0-9.355-1.457-12.125-4.379z"/>
<path d="M284.067,48.667c1.969,0,4.207-1.535,6.711-4.605c2.5-3.07,3.754-5.555,3.754-7.453c0-1.969-1.309-4.453-3.926-7.449c-2.617-2.992-4.648-4.492-6.086-4.492c-1.594,0-3.695,1.555-6.313,4.664s-3.926,5.766-3.926,7.961c0,2.352,1.137,4.836,3.41,7.453s4.398,3.922,6.375,3.922z"/>
</g>
</glyph>
<glyph id="k" unicode="k" glyph-name="k" horiz-adv-x="120">
<path transform="scale(1,-1)translate(-310, -170)"
d="M331.507,147.307V35.413c0-8.078-0.68-13.219-2.031-15.43s-3.906-3.316-7.664-3.316h-1.805c-1.387,0-2.465-0.242-3.23-0.734c-0.77-0.492-1.191-1.188-1.27-2.094c0-1.656,1.977-2.941,5.93-3.848l0.23-0.074
c1.824-0.301,3.516-0.68,5.074-1.133s3.098-0.984,4.617-1.594c2.66-1.059,5.586-2.535,8.781-4.43c3.191-1.895,5.246-2.844,6.16-2.844c0.984,0,1.746,0.383,2.277,1.141s0.801,1.859,0.801,3.301c0,0.305-0.039,1.082-0.113,2.332
c-0.078,1.254-0.113,2.375-0.113,3.359c-0.383,5.391-0.668,10.684-0.859,15.883s-0.285,10.531-0.285,15.996v80.641l33.148-30.207c1.434-1.367,2.566-2.715,3.398-4.047c0.832-1.328,1.25-2.527,1.25-3.594c0-1.289-1.324-2.316-3.969-3.078
c-0.305-0.074-0.566-0.148-0.793-0.227c-1.891-0.375-3.215-0.828-3.969-1.359c-0.758-0.527-1.133-1.242-1.133-2.148c0-0.68,0.453-1.262,1.359-1.754s2.004-0.738,3.289-0.738c0.301,0,2.305,0.211,6.008,0.625c3.703,0.418,7.297,0.625,10.773,0.625
c2.871,0,6.141-0.207,9.809-0.625c3.664-0.414,5.875-0.625,6.633-0.625c1.438,0,2.496,0.227,3.176,0.68s1.02,1.133,1.02,2.039c0,1.734-1.285,2.828-3.855,3.281h-0.113c-1.133,0.152-2.27,0.379-3.402,0.684s-2.305,0.723-3.516,1.254
c-7.332,2.891-13.758,7.07-19.273,12.543c-0.605,0.684-1.059,1.141-1.359,1.367l-19.73,17.781c10.66,14.914,19.223,26.215,25.688,33.902s11.59,12.672,15.371,14.953c3.023,1.75,6.879,2.969,11.566,3.652c0.375,0.078,0.641,0.113,0.793,0.113
c2.191,0.152,3.609,0.438,4.254,0.852c0.641,0.414,1,1.113,1.078,2.094c0,1.133-0.512,1.922-1.535,2.375s-3.012,0.68-5.965,0.68h-19.277c-5,0-15.23-10.113-30.684-30.34c-5.609-7.375-10.117-13.227-13.523-17.563l-6.516,6.156v15.617
c0,6.852,0.531,11.344,1.602,13.477c1.066,2.133,3.086,3.883,6.059,5.25c1.219,0.535,3.121,0.992,5.715,1.371c0.078,0.023,0.152,0.031,0.23,0.031c2.133,0.152,3.523,0.492,4.172,1.023s0.973,1.363,0.973,2.5c0,0.836-0.344,1.496-1.027,1.988
s-1.594,0.738-2.734,0.738c-0.305,0-2.758-0.211-7.355-0.625c-4.602-0.414-8.992-0.625-13.172-0.625c-6.309,0-12.313,0.375-18.016,1.125c-0.914,0.082-1.445,0.125-1.594,0.125c-0.836,0-1.523-0.25-2.055-0.746s-0.797-1.09-0.797-1.777
c0-0.766,0.262-1.473,0.789-2.121c0.523-0.648,1.613-1.434,3.27-2.355c0.375-0.227,0.789-0.492,1.242-0.797c1.273-0.758,2.215-1.445,2.816-2.055c1.277-1.367,2.16-3.074,2.648-5.129c0.488-2.051,0.734-5.926,0.734-11.629z"/>
</glyph>
<glyph unicode="*" glyph-name="*" horiz-adv-x="120">
<g transform="scale(1, -1)">
<use xlink:href="#Batik_Squiggle" width="108" height="114"
transform="translate(0,-130)" />
</g>
</glyph>
<hkern g1="B" g2="a" k="5"/>
<hkern g1="a" g2="t" k="14"/>
<hkern g1="a" g2="ti" k="14"/>
<hkern g1="i" g2="k" k="6"/>
<hkern g1="ti" g2="k" k="6"/>
</font>
<g id="Batik_Logo_Underline" transform="scale(.75,.75)" >
<path d="M37.886,60c-0.018,0.1-0.377,1.375-0.439,1.492c-0.15,0.285-1.382,2.046-1.598,2.291c0.206-0.233,0.428-0.452,0.65-0.67c-6.851,6.751-0.262,0.713,0.893-0.499c1.893-1.986-2.124,1.712,0.112-0.08
c0.604-0.484,1.242-0.925,1.886-1.355c-2.574,1.719,0.458-0.228,1.417-0.868c-2.634,1.761-1.231,0.788-0.605,0.423c1.799-1.049,3.686-1.946,5.591-2.783c0.978-0.43,1.97-0.828,2.964-1.217c1.844-0.723-1.918,0.683-0.003,0.012
c0.706-0.264,1.412-0.528,2.117-0.792c-1.224,0.456-1.388,0.521-0.491,0.195c2.531-0.908,5.102-1.708,7.683-2.461c5.73-1.672,11.556-3.013,17.401-4.216c30.689-6.315,61.555-8.765,92.723-10.467c35.225-1.924,70.559-2.313,105.819-1.278
c27.375,0.803,55.137,2.029,82.154,6.813c1.854,0.328,3.702,0.69,5.545,1.079c-2.182-0.459,0.632,0.149,1.102,0.26c0.785,0.185,1.566,0.383,2.347,0.585c2.714,0.705,5.407,1.537,7.987,2.642c0.676-4.98,1.351-9.959,2.026-14.939
c-29.001,20.428-70.184,18.783-104.484,20.881c-37.85,2.314-78.422,7.341-105.371,37.024c-3.142,3.46-5.693,10.35-0.21,12.998c8.018,3.873,16.683,5.137,25.266,7.166c7.149,1.69,13.362,4.381,16.934,11.121c4.934,9.311,2.75,18.519-0.175,28.003
c-3.217,10.428-5.508,20.886-0.692,31.219c4.219,9.05,19.441-3.641,15.823-11.611c-4.234-9.326,1.407-19.828,3.653-28.997c2.667-10.888,1.908-22.401-3.872-32.224c-9.76-16.588-31.066-13.848-46.449-21.271c-0.07,4.333-0.14,8.666-0.21,12.998
c10.537-11.719,25.017-18.668,40.974-22.714c18.159-4.604,37.034-5.719,55.666-6.747c37.146-2.049,77.822-2.405,109.506-24.634c4.136-2.902,8.771-12.048,2.026-14.939c-7.868-3.373-16.687-4.781-25.083-6.132c-12.447-2.004-25.032-3.156-37.6-4.075
c-33.215-2.427-66.599-2.839-99.887-2.247c-34.872,0.621-69.791,2.496-104.432,6.637c-24.317,2.907-50.972,6.112-73.187,17.171c-4.951,2.465-9.505,5.587-13.309,9.623c-1.027,1.089-2.19,2.464-2.986,3.643c0.137-0.203-3.419,6.639-1.518,3.165
c-0.205,0.374-0.38,0.762-0.549,1.151c-1.126,2.59-2.056,5.322-2.196,8.168c-0.222,4.484,4.48,3.091,6.917,1.551c3.856-2.437,7.345-6.516,8.167-11.093z"/>
</g> <!-- End Batik_Logo_Underline -->
<filter id="dropShadow" filterUnits="objectBoundingBox"
filterRes="200" width="1.4" height="1.4">
<feGaussianBlur in="SourceAlpha" stdDeviation="4" />
<feOffset dx="8" dy="8" />
<feComponentTransfer result="shadow">
<feFuncA type="linear" slope=".5" intercept="0" />
</feComponentTransfer>
<feComposite in2="shadow" in="SourceGraphic"/>
</filter>
<g id="Batik_Logo_Shadow" filter="url(#dropShadow)">
<g id="Batik_Logo">
<text id="text" x="0" y="0" font-family="Batik SVGFont"
font-size="180">Batik</text>
<use xlink:href="#Batik_Logo_Underline"/>
</g> <!-- End Batik_Logo -->
</g> <!-- End Batik_Logo_Shadow -->
<g id="Batik_Tag_Box" >
<rect x="1" y="1" width="446" height="496"
style="fill:none; stroke:black" />
<use xlink:href="#Batik_Squiggle" width="27" height="28"
transform="translate(418,467)" />
</g> <!-- End Batik_Tag_Box -->
</defs>
<use x="65" y="233" xlink:href="#Batik_Logo_Shadow" />
</g>
<!-- ============================================================= -->
<!-- Batik sample mark -->
<!-- ============================================================= -->
<use xlink:href="#Batik_Tag_Box" />
</svg>

Binary file not shown.

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

25
imageio/imageio-ico/license.txt Executable file
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,29 @@
<?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-ico</artifactId>
<version>2.3-SNAPSHOT</version>
<name>TwelveMonkeys ImageIO ICO plugin</name>
<description>ImageIO plugin for Windows Icon (ICO) and Cursor (CUR) format.</description>
<parent>
<artifactId>twelvemonkeys-imageio</artifactId>
<groupId>com.twelvemonkeys</groupId>
<version>2.3-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,72 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import com.twelvemonkeys.lang.Validate;
import java.awt.image.BufferedImage;
/**
* Describes a bitmap structure.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: Bitmap.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
abstract class BitmapDescriptor {
protected final DirectoryEntry mEntry;
protected final DIBHeader mHeader;
protected BufferedImage mImage;
public BitmapDescriptor(final DirectoryEntry pEntry, final DIBHeader pHeader) {
Validate.notNull(pEntry, "entry");
Validate.notNull(pHeader, "header");
mEntry = pEntry;
mHeader = pHeader;
}
abstract public BufferedImage getImage();
public final int getWidth() {
return mEntry.getWidth();
}
public final int getHeight() {
return mEntry.getHeight();
}
protected final int getColorCount() {
return mEntry.getColorCount() != 0 ? mEntry.getColorCount() : 1 << getBitCount();
}
protected final int getBitCount() {
return mEntry.getBitCount() != 0 ? mEntry.getBitCount() : mHeader.getBitCount();
}
}

View File

@@ -0,0 +1,182 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import com.twelvemonkeys.image.InverseColorMapIndexColorModel;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.util.Hashtable;
/**
* Describes an indexed bitmap structure (1, 4, or 8 bits per pixes).
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BitmapIndexed.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapIndexed extends BitmapDescriptor {
protected final int[] mBits;
protected final int[] mColors;
private BitmapMask mMask;
public BitmapIndexed(final DirectoryEntry pEntry, final DIBHeader pHeader) {
super(pEntry, pHeader);
mBits = new int[getWidth() * getHeight()];
// NOTE: We're adding space for one extra color, for transparency
mColors = new int[getColorCount() + 1];
}
public BufferedImage createImageIndexed() {
// TODO: This is very stupid, maybe we need a TYPE_CUSTOM image, with separate alphaRaster?!
// As ICO has a separate bitmask, not related to palette index (allows 256 colors + trans) :-P
IndexColorModel icm = createColorModel();
// This is slightly obscure, and should probably be moved..
Hashtable<String, Object> properties = null;
if (mEntry instanceof DirectoryEntry.CUREntry) {
DirectoryEntry.CUREntry entry = (DirectoryEntry.CUREntry) mEntry;
properties = new Hashtable<String, Object>(1);
properties.put("cursor_hotspot", entry.getHotspot());
}
BufferedImage image = new BufferedImage(
icm,
icm.createCompatibleWritableRaster(getWidth(), getHeight()),
icm.isAlphaPremultiplied(), properties
);
WritableRaster raster = image.getRaster();
// Make pixels transparant according to mask
final int trans = icm.getTransparentPixel();
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
if (mMask.isTransparent(x, y)) {
mBits[x + getWidth() * y] = trans;
}
}
}
raster.setSamples(0, 0, getWidth(), getHeight(), 0, mBits);
//System.out.println("Image: " + image);
return image;
}
/**
* @return Color model created from color palette in entry
*/
IndexColorModel createColorModel() {
// NOTE: This is a hack to make room for transparent pixel for mask
int bits = getBitCount();
int colors = mColors.length;
int trans = -1;
// Try to avoid USHORT transfertype, as it results in BufferedImage TYPE_CUSTOM
// NOTE: This code assumes icons are small, and is NOT optimized for performance...
if (colors > (1 << getBitCount())) {
int index = BitmapIndexed.findTransIndexMaybeRemap(mColors, mBits);
if (index == -1) {
// No duplicate found, increase bitcount
bits++;
trans = mColors.length - 1;
}
else {
// Found a duplicate, use it as trans
trans = index;
colors--;
}
}
// NOTE: Setting hasAlpha to true, makes things work on 1.2
return new InverseColorMapIndexColorModel(
bits, colors, mColors, 0, true, trans,
bits <= 8 ? DataBuffer.TYPE_BYTE : DataBuffer.TYPE_USHORT
);
}
private static int findTransIndexMaybeRemap(final int[] pColors, final int[] pBits) {
// Look for unused colors, to use as transparent
final boolean[] used = new boolean[pColors.length - 1];
for (int pBit : pBits) {
if (!used[pBit]) {
used[pBit] = true;
}
}
for (int i = 0; i < used.length; i++) {
if (!used[i]) {
return i;
}
}
// Try to find duplicates in colormap, and remap
int trans = -1;
int duplicate = -1;
for (int i = 0; trans == -1 && i < pColors.length - 1; i++) {
for (int j = i + 1; j < pColors.length - 1; j++) {
if (pColors[i] == pColors[j]) {
trans = j;
duplicate = i;
break;
}
}
}
if (trans != -1) {
// Remap duplicate
for (int i = 0; i < pBits.length; i++) {
if (pBits[i] == trans) {
pBits[i] = duplicate;
}
}
}
return trans;
}
public BufferedImage getImage() {
if (mImage == null) {
mImage = createImageIndexed();
}
return mImage;
}
public void setMask(final BitmapMask pMask) {
mMask = pMask;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import java.awt.image.BufferedImage;
/**
* Describes a transparency mask structure (1 bit).
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BitmapMask.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapMask extends BitmapDescriptor {
protected final BitmapIndexed mMask;
public BitmapMask(final DirectoryEntry pParent, final DIBHeader pHeader) {
super(pParent, pHeader);
mMask = new BitmapIndexed(pParent, pHeader);
}
boolean isTransparent(final int pX, final int pY) {
// NOTE: 1: Fully transparent, 0: Opaque...
return mMask.mBits[pX + pY * getWidth()] != 0;
}
public BufferedImage getImage() {
return mMask.getImage();
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import java.awt.image.BufferedImage;
/**
* Describes an RGB/true color bitmap structure (16, 24 and 32 bits per pixel).
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BitmapRGB.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapRGB extends BitmapDescriptor {
public BitmapRGB(final DirectoryEntry pEntry, final DIBHeader pHeader) {
super(pEntry, pHeader);
}
public BufferedImage getImage() {
return mImage;
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import java.awt.image.BufferedImage;
/**
* Represents bitmap structures we can't read.
* Allows for deferred exception handling, and allowing clients to read all images that can be read.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BitmapUnsupported.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class BitmapUnsupported extends BitmapDescriptor {
private String mMessage;
public BitmapUnsupported(final DirectoryEntry pEntry, final String pMessage) {
super(pEntry, null);
mMessage = pMessage;
}
public BufferedImage getImage() {
throw new IllegalStateException(mMessage);
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.io.IOException;
/**
* ImageReader for Microsoft Windows CUR (cursor) format.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: CURImageReader.java,v 1.0 Apr 20, 2009 11:54:28 AM haraldk Exp$
*
* @see com.twelvemonkeys.imageio.plugins.ico.ICOImageReader
*/
public class CURImageReader extends ICOImageReader {
// NOTE: All implementation is part of the ICOImageReader
public CURImageReader() {
super(DIB.TYPE_CUR);
}
protected CURImageReader(final ImageReaderSpi pProvider) {
super(pProvider);
}
/**
* Returns the hot spot location for the cursor.
*
* @param pImageIndex the index of the cursor in the current input.
* @return the hot spot location for the cursor
*
* @throws IOException if an I/O exception occurs during reading of image meta data
* @throws IndexOutOfBoundsException if {@code pImageIndex} is less than {@code 0} or greater than/equal to
* the number of cursors in the file
*/
public final Point getHotSpot(final int pImageIndex) throws IOException {
DirectoryEntry.CUREntry entry = (DirectoryEntry.CUREntry) getEntry(pImageIndex);
return entry.getHotspot();
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* CURImageReaderSpi
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CURImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
public class CURImageReaderSpi extends ImageReaderSpi {
public CURImageReaderSpi() {
this(IIOUtil.getProviderInfo(CURImageReaderSpi.class));
}
private CURImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[]{"cur", "CUR"},
new String[]{"cur"},
new String[]{
"image/vnd.microsoft.cursor", // Official IANA MIME
"image/x-cursor", // Common extension MIME
"image/cursor" // Unofficial, but common
},
"com.twelvemonkeys.imageio.plugins.ico.CURImageReader",
STANDARD_INPUT_TYPE,
null,
true, null, null, null, null,
true,
null, null,
null, null
);
}
public boolean canDecodeInput(final Object pSource) throws IOException {
return pSource instanceof ImageInputStream && ICOImageReaderSpi.canDecode((ImageInputStream) pSource, DIB.TYPE_CUR);
}
public ImageReader createReaderInstance(final Object pExtension) throws IOException {
return new CURImageReader(this);
}
public String getDescription(final Locale pLocale) {
return "Windows Cursor Format (CUR) Reader";
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
/**
* DIB
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: DIB.java,v 1.0 Apr 8, 2008 1:43:04 PM haraldk Exp$
*
* @see <a href="http://en.wikipedia.org/wiki/BMP_file_format">BMP file format (Wikipedia)</a>
* @see <a href="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)">ICO file format (Wikipedia)</a>
*/
interface DIB {
int TYPE_UNKNOWN = 0;
int TYPE_ICO = 1;
int TYPE_CUR = 2;
/** BITMAPCOREHEADER size, OS/2 V1 */
int OS2_V1_HEADER_SIZE = 12;
/** BITMAPCOREHEADER size, OS/2 V2 */
int OS2_V2_HEADER_SIZE = 64;
/**
* BITMAPINFOHEADER size, Windows 3.0 and later.
* This is the most commonly used header for persistent bitmaps
*/
int WINDOWS_V3_HEADER_SIZE = 40;
/** BITMAPV4HEADER size, Windows 95/NT4 and later */
int WINDOWS_V4_HEADER_SIZE = 108;
/** BITMAPV5HEADER size, Windows 98/2000 and later */
int WINDOWS_V5_HEADER_SIZE = 124;
/** PNG "magic" identifier */
long PNG_MAGIC = 0x89l << 56 | (long) 'P' << 48 | (long) 'N' << 40 | (long) 'G' << 32 | 0x0dl << 24 | 0x0al << 16 | 0x1al << 8 | 0x0al;
}

View File

@@ -0,0 +1,197 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException;
/**
* Represents the DIB (Device Independent Bitmap) Information header structure.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: DIBHeader.java,v 1.0 May 5, 2009 10:45:31 AM haraldk Exp$
* @see <a href="http://en.wikipedia.org/wiki/BMP_file_format">BMP file format (Wikipedia)</a>
*/
abstract class DIBHeader {
protected int mSize;
protected int mWidth;
// NOTE: If a bitmask is present, this value includes the height of the mask
// (so often header.height = entry.height * 2)
protected int mHeight;
protected int mPlanes;
protected int mBitCount;
/**
* 0 = BI_RGB: No compression
* 1 = BI_RLE8: 8 bit RLE Compression (8 bit only)
* 2 = BI_RLE4: 4 bit RLE Compression (4 bit only)
* 3 = BI_BITFIELDS: No compression (16 & 32 bit only)
*/
protected int mCompression;
// May be 0 if not known
protected int mImageSize;
protected int mXPixelsPerMeter;
protected int mYPixelsPerMeter;
protected int mColorsUsed;
// 0 means all colors are important
protected int mColorsImportant;
protected DIBHeader() {
}
public static DIBHeader read(final DataInput pStream) throws IOException {
int size = pStream.readInt();
// ICO always uses the Microsoft Windows V3 DIB header, which is 40 bytes
DIBHeader header = createHeader(size);
header.read(size, pStream);
return header;
}
private static DIBHeader createHeader(final int pSize) throws IOException {
switch (pSize) {
case DIB.OS2_V1_HEADER_SIZE:
case DIB.OS2_V2_HEADER_SIZE:
throw new IIOException(String.format("OS/2 Bitmap Information Header (size: %s) not supported", pSize));
case DIB.WINDOWS_V3_HEADER_SIZE:
return new WindowsV3DIBHeader();
case DIB.WINDOWS_V4_HEADER_SIZE:
case DIB.WINDOWS_V5_HEADER_SIZE:
throw new IIOException(String.format("Windows Bitmap Information Header (size: %s) not supported", pSize));
default:
throw new IIOException(String.format("Unknown Bitmap Information Header (size: %s)", pSize));
}
}
protected abstract void read(int pSize, DataInput pStream) throws IOException;
public final int getSize() {
return mSize;
}
public final int getWidth() {
return mWidth;
}
public final int getHeight() {
return mHeight;
}
public final int getPlanes() {
return mPlanes;
}
public final int getBitCount() {
return mBitCount;
}
public int getCompression() {
return mCompression;
}
public int getImageSize() {
return mImageSize;
}
public int getXPixelsPerMeter() {
return mXPixelsPerMeter;
}
public int getYPixelsPerMeter() {
return mYPixelsPerMeter;
}
public int getColorsUsed() {
return mColorsUsed;
}
public int getColorsImportant() {
return mColorsImportant;
}
public String toString() {
return String.format(
"%s: size: %d bytes, " +
"width: %d, height: %d, planes: %d, bit count: %d, compression: %d, " +
"image size: %d%s, " +
"X pixels per m: %d, Y pixels per m: %d, " +
"colors used: %d, colors important: %d%s",
getClass().getSimpleName(),
getSize(), getWidth(), getHeight(), getPlanes(), getBitCount(), getCompression(),
getImageSize(), (getImageSize() == 0 ? " (unknown)" : ""),
getXPixelsPerMeter(), getYPixelsPerMeter(),
getColorsUsed(), getColorsImportant(), (getColorsImportant() == 0 ? " (all)" : "")
);
}
/**
* Represents the DIB (Device Independent Bitmap) Windows V3 Bitmap Information header structure.
* This is the common format for persistent DIB structures, even if Windows
* may use the later versions at run-time.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: DIBHeader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
* @see <a href="http://en.wikipedia.org/wiki/BMP_file_format">BMP file format (Wikipedia)</a>
*/
static final class WindowsV3DIBHeader extends DIBHeader {
protected void read(final int pSize, final DataInput pStream) throws IOException {
if (pSize != DIB.WINDOWS_V3_HEADER_SIZE) {
throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.WINDOWS_V3_HEADER_SIZE));
}
mSize = pSize;
mWidth = pStream.readInt();
mHeight = pStream.readInt();
mPlanes = pStream.readUnsignedShort();
mBitCount = pStream.readUnsignedShort();
mCompression = pStream.readInt();
mImageSize = pStream.readInt();
mXPixelsPerMeter = pStream.readInt();
mYPixelsPerMeter = pStream.readInt();
mColorsUsed = pStream.readInt();
mColorsImportant = pStream.readInt();
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import java.io.DataInput;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* Directory
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: Directory.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
class Directory {
private final List<DirectoryEntry> mEntries;
private Directory(int pImageCount) {
mEntries = Arrays.asList(new DirectoryEntry[pImageCount]);
}
public static Directory read(final int pType, final int pImageCount, final DataInput pStream) throws IOException {
Directory directory = new Directory(pImageCount);
directory.readEntries(pType, pStream);
return directory;
}
private void readEntries(final int pType, final DataInput pStream) throws IOException {
for (int i = 0; i < mEntries.size(); i++) {
mEntries.set(i, DirectoryEntry.read(pType, pStream));
}
}
public DirectoryEntry getEntry(final int pEntryIndex) {
return mEntries.get(pEntryIndex);
}
public int count() {
return mEntries.size();
}
@Override
public String toString() {
return String.format("%s%s", getClass().getSimpleName(), mEntries);
}
}

View File

@@ -0,0 +1,165 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException;
import java.awt.image.BufferedImage;
import java.awt.*;
/**
* DirectoryEntry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: DirectoryEntry.java,v 1.0 Apr 4, 2009 4:29:53 PM haraldk Exp$
* @see <a href="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)#Directory">Wikipedia</a>
*/
abstract class DirectoryEntry {
private int mWidth;
private int mHeight;
private int mColorCount;
int mPlanes;
int mBitCount;
private int mSize;
private int mOffset;
private DirectoryEntry() {
}
public static DirectoryEntry read(final int pType, final DataInput pStream) throws IOException {
DirectoryEntry entry = createEntry(pType);
entry.read(pStream);
return entry;
}
private static DirectoryEntry createEntry(int pType) throws IIOException {
switch (pType) {
case DIB.TYPE_ICO:
return new ICOEntry();
case DIB.TYPE_CUR:
return new CUREntry();
default:
throw new IIOException(
String.format(
"Unknown DIB type: %s, expected: %s (ICO) or %s (CUR)",
pType, DIB.TYPE_ICO, DIB.TYPE_CUR
)
);
}
}
protected void read(final DataInput pStream) throws IOException {
// Width/height = 0, means 256
int w = pStream.readUnsignedByte();
mWidth = w == 0 ? 256 : w;
int h = pStream.readUnsignedByte();
mHeight = h == 0 ? 256 : h;
// Color count = 0, means 256 or more colors
mColorCount = pStream.readUnsignedByte();
// Ignore. Should be 0, but .NET (System.Drawing.Icon.Save) sets this value to 255, according to Wikipedia
pStream.readUnsignedByte();
mPlanes = pStream.readUnsignedShort(); // Should be 0 or 1 for ICO, x hotspot for CUR
mBitCount = pStream.readUnsignedShort(); // bit count for ICO, y hotspot for CUR
// Size of bitmap in bytes
mSize = pStream.readInt();
mOffset = pStream.readInt();
}
public String toString() {
return String.format(
"%s: width: %d, height: %d, colors: %d, planes: %d, bit count: %d, size: %d, offset: %d",
getClass().getSimpleName(),
mWidth, mHeight, mColorCount, mPlanes, mBitCount, mSize, mOffset
);
}
public int getBitCount() {
return mBitCount;
}
public int getColorCount() {
return mColorCount;
}
public int getHeight() {
return mHeight;
}
public int getOffset() {
return mOffset;
}
public int getPlanes() {
return mPlanes;
}
public int getSize() {
return mSize;
}
public int getWidth() {
return mWidth;
}
/**
* Cursor directory entry.
*/
static class CUREntry extends DirectoryEntry {
private int mXHotspot;
private int mYHotspot;
@Override
protected void read(final DataInput pStream) throws IOException {
super.read(pStream);
// NOTE: This is a hack...
mXHotspot = mPlanes;
mYHotspot = mBitCount;
mPlanes = 1; // Always 1 for all BMP types
mBitCount = 0;
}
public Point getHotspot() {
return new Point(mXHotspot, mYHotspot);
}
}
/**
* Icon directory entry.
*/
static final class ICOEntry extends DirectoryEntry {
}
}

View File

@@ -0,0 +1,709 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
import com.twelvemonkeys.util.WeakWeakMap;
import javax.imageio.*;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
import java.util.*;
import java.util.List;
/**
* ImageReader for Microsoft Windows ICO (icon) format.
* 1, 4, 8 bit palette support with bitmask transparency, and 16, 24 and 32 bit
* true color support with alpha. Also supports Windows Vista PNG encoded icons.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ICOImageReader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*
* @see <a href="http://en.wikipedia.org/wiki/BMP_file_format">BMP file format (Wikipedia)</a>
* @see <a href="http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)">ICO file format (Wikipedia)</a>
*/
// SEE http://en.wikipedia.org/wiki/ICO_(icon_image_file_format)
// TODO: Decide wether DirectoryEntry or DIBHeader should be primary source for color count/bit count
// TODO: Support loading icons from DLLs, see
// <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwui/html/msdn_icons.asp">MSDN</a>
// Known issue: 256x256 PNG encoded icons does not have IndexColorModel even if stated in DirectoryEntry (seem impossible as the PNGs are all true color)
public class ICOImageReader extends ImageReaderBase {
// TODO: Consider moving the reading to inner classes (subclasses of BitmapDescriptor)
private Directory mDirectory;
// TODO: Review these, make sure we don't have a memory leak
private Map<DirectoryEntry, DIBHeader> mHeaders = new WeakHashMap<DirectoryEntry, DIBHeader>();
private Map<DirectoryEntry, BitmapDescriptor> mDescriptors = new WeakWeakMap<DirectoryEntry, BitmapDescriptor>();
private ImageReader mPNGImageReader;
public ICOImageReader() {
this(DIB.TYPE_ICO);
}
ICOImageReader(final int pType) {
this(createProviderForConstructor(pType));
}
protected ICOImageReader(final ImageReaderSpi pProvider) {
super(pProvider);
}
private static ImageReaderSpi createProviderForConstructor(final int pType) {
switch (pType) {
case DIB.TYPE_ICO:
return new ICOImageReaderSpi();
case DIB.TYPE_CUR:
return new CURImageReaderSpi();
default:
throw new IllegalArgumentException(String.format("Unsupported ICO/CUR type: %d", pType));
}
}
protected void resetMembers() {
mDirectory = null;
mHeaders.clear();
mDescriptors.clear();
if (mPNGImageReader != null) {
mPNGImageReader.dispose();
mPNGImageReader = null;
}
}
public Iterator<ImageTypeSpecifier> getImageTypes(final int pImageIndex) throws IOException {
DirectoryEntry entry = getEntry(pImageIndex);
// NOTE: Delegate to PNG reader
if (isPNG(entry)) {
return getImageTypesPNG(entry);
}
List<ImageTypeSpecifier> types = new ArrayList<ImageTypeSpecifier>();
DIBHeader header = getHeader(entry);
// Use data from header to create specifier
ImageTypeSpecifier specifier;
switch (header.getBitCount()) {
case 1:
case 2:
case 4:
case 8:
// TODO: This is slightly QnD...
int offset = entry.getOffset() + header.getSize();
if (offset != mImageInput.getStreamPosition()) {
mImageInput.seek(offset);
}
BitmapIndexed indexed = new BitmapIndexed(entry, header);
readColorMap(indexed);
specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(indexed.createColorModel());
break;
case 16:
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
break;
case 24:
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
break;
case 32:
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
break;
default:
throw new IIOException(String.format("Unknown bit depth: %d", header.getBitCount()));
}
types.add(specifier);
return types.iterator();
}
@Override
public int getNumImages(final boolean pAllowSearch) throws IOException {
return getDirectory().count();
}
public int getWidth(final int pImageIndex) throws IOException {
return getEntry(pImageIndex).getWidth();
}
public int getHeight(final int pImageIndex) throws IOException {
return getEntry(pImageIndex).getHeight();
}
public BufferedImage read(final int pImageIndex, final ImageReadParam pParam) throws IOException {
checkBounds(pImageIndex);
processImageStarted(pImageIndex);
DirectoryEntry entry = getEntry(pImageIndex);
BufferedImage destination;
if (isPNG(entry)) {
// NOTE: Special case for Windows Vista, 256x256 PNG encoded images, with no DIB header...
destination = readPNG(entry, pParam);
}
else {
// NOTE: If param does not have explicit destination, we'll try to create a BufferedImage later,
// to allow for storing the cursor hotspot for CUR images
destination = hasExplicitDestination(pParam) ?
getDestination(pParam, getImageTypes(pImageIndex), getWidth(pImageIndex), getHeight(pImageIndex)) :
null;
BufferedImage image = readBitmap(entry);
// TODO: Handle AOI and subsampling inline, probably not of big importance...
if (pParam != null) {
image = fakeAOI(image, pParam);
image = ImageUtil.toBuffered(fakeSubsampling(image, pParam));
}
if (destination == null) {
// This is okay, as long as the client did not request explicit destination image/type
destination = image;
}
else {
Graphics2D g = destination.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(image, 0, 0, null);
}
finally {
g.dispose();
}
}
}
processImageProgress(100);
processImageComplete();
return destination;
}
private boolean hasExplicitDestination(final ImageReadParam pParam) {
return (pParam != null && (pParam.getDestination() != null || pParam.getDestinationType() != null || pParam.getDestinationOffset() != null));
}
private boolean isPNG(final DirectoryEntry pEntry) throws IOException {
long magic;
mImageInput.seek(pEntry.getOffset());
mImageInput.setByteOrder(ByteOrder.BIG_ENDIAN);
try {
magic = mImageInput.readLong();
}
finally {
mImageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
}
return magic == DIB.PNG_MAGIC;
}
private BufferedImage readPNG(final DirectoryEntry pEntry, final ImageReadParam pParam) throws IOException {
// TODO: Consider delegating listener calls
return initPNGReader(pEntry).read(0, pParam);
}
private Iterator<ImageTypeSpecifier> getImageTypesPNG(final DirectoryEntry pEntry) throws IOException {
return initPNGReader(pEntry).getImageTypes(0);
}
private ImageReader initPNGReader(final DirectoryEntry pEntry) throws IOException {
ImageReader pngReader = getPNGReader();
mImageInput.seek(pEntry.getOffset());
InputStream inputStream = IIOUtil.createStreamAdapter(mImageInput, pEntry.getSize());
ImageInputStream stream = ImageIO.createImageInputStream(inputStream);
// NOTE: Will throw IOException on later reads if input is not PNG
pngReader.setInput(stream);
return pngReader;
}
private ImageReader getPNGReader() throws IIOException {
// TODO: Prefer Sun's std JDK PNGImagerReader, because it has known behaviour?
if (mPNGImageReader == null) {
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("PNG");
if (readers.hasNext()) {
mPNGImageReader = readers.next();
}
else {
throw new IIOException("No PNGImageReader found using ImageIO, can't read PNG encoded ICO format.");
}
}
else {
mPNGImageReader.reset();
}
return mPNGImageReader;
}
private DIBHeader getHeader(final DirectoryEntry pEntry) throws IOException {
if (!mHeaders.containsKey(pEntry)) {
mImageInput.seek(pEntry.getOffset());
DIBHeader header = DIBHeader.read(mImageInput);
mHeaders.put(pEntry, header);
}
return mHeaders.get(pEntry);
}
private BufferedImage readBitmap(final DirectoryEntry pEntry) throws IOException {
// TODO: Get rid of the caching, as the images are mutable
BitmapDescriptor descriptor = mDescriptors.get(pEntry);
if (descriptor == null || !mDescriptors.containsKey(pEntry)) {
DIBHeader header = getHeader(pEntry);
int offset = pEntry.getOffset() + header.getSize();
if (offset != mImageInput.getStreamPosition()) {
mImageInput.seek(offset);
}
// TODO: Support this, it's already in the BMP reader, spec allows RLE4 and RLE8
if (header.getCompression() != 0) {
descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported compression: %d", header.getCompression()));
}
else {
int bitCount = header.getBitCount();
switch (bitCount) {
// Palette style
case 1:
case 4:
case 8:
descriptor = new BitmapIndexed(pEntry, header);
readBitmapIndexed((BitmapIndexed) descriptor);
break;
// RGB style
case 16:
descriptor = new BitmapRGB(pEntry, header);
readBitmap16(descriptor);
break;
case 24:
descriptor = new BitmapRGB(pEntry, header);
readBitmap24(descriptor);
break;
case 32:
descriptor = new BitmapRGB(pEntry, header);
readBitmap32(descriptor);
break;
default:
descriptor = new BitmapUnsupported(pEntry, String.format("Unsupported bit count %d", bitCount));
}
}
mDescriptors.put(pEntry, descriptor);
}
return descriptor.getImage();
}
private void readBitmapIndexed(final BitmapIndexed pBitmap) throws IOException {
readColorMap(pBitmap);
switch (pBitmap.getBitCount()) {
case 1:
readBitmapIndexed1(pBitmap, false);
break;
case 4:
readBitmapIndexed4(pBitmap);
break;
case 8:
readBitmapIndexed8(pBitmap);
break;
}
BitmapMask mask = new BitmapMask(pBitmap.mEntry, pBitmap.mHeader);
readBitmapIndexed1(mask.mMask, true);
pBitmap.setMask(mask);
}
private void readColorMap(final BitmapIndexed pBitmap) throws IOException {
int colorCount = pBitmap.getColorCount();
for (int i = 0; i < colorCount; i++) {
// aRGB (a is "Reserved")
pBitmap.mColors[i] = (mImageInput.readInt() & 0xffffff) | 0xff000000;
}
}
private void readBitmapIndexed1(final BitmapIndexed pBitmap, final boolean pAsMask) throws IOException {
int width = adjustToPadding(pBitmap.getWidth() >> 3);
byte[] row = new byte[width];
for (int y = 0; y < pBitmap.getHeight(); y++) {
mImageInput.readFully(row, 0, width);
int rowPos = 0;
int xOrVal = 0x80;
int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
for (int x = 0; x < pBitmap.getWidth(); x++) {
pBitmap.mBits[pos++] = ((row[rowPos] & xOrVal) / xOrVal) & 0xFF;
if (xOrVal == 1) {
xOrVal = 0x80;
rowPos++;
}
else {
xOrVal >>= 1;
}
}
// NOTE: If we are reading the mask, we don't abort or progress
if (!pAsMask) {
if (abortRequested()) {
processReadAborted();
break;
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
}
}
private void readBitmapIndexed4(final BitmapIndexed pBitmap) throws IOException {
int width = adjustToPadding(pBitmap.getWidth() >> 1);
byte[] row = new byte[width];
for (int y = 0; y < pBitmap.getHeight(); y++) {
mImageInput.readFully(row, 0, width);
int rowPos = 0;
boolean high4 = true;
int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
for (int x = 0; x < pBitmap.getWidth(); x++) {
int value;
if (high4) {
value = (row[rowPos] & 0xF0) >> 4;
}
else {
value = row[rowPos] & 0x0F;
rowPos++;
}
pBitmap.mBits[pos++] = value & 0xFF;
high4 = !high4;
}
if (abortRequested()) {
processReadAborted();
break;
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
}
private void readBitmapIndexed8(final BitmapIndexed pBitmap) throws IOException {
int width = adjustToPadding(pBitmap.getWidth());
byte[] row = new byte[width];
for (int y = 0; y < pBitmap.getHeight(); y++) {
mImageInput.readFully(row, 0, width);
int rowPos = 0;
int pos = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
for (int x = 0; x < pBitmap.getWidth(); x++) {
pBitmap.mBits[pos++] = row[rowPos++] & 0xFF;
}
if (abortRequested()) {
processReadAborted();
break;
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
}
/**
* @param pWidth Bytes per scan line (i.e., 1BPP, width = 9 -> bytes = 1)
* @return padded width
*/
private static int adjustToPadding(final int pWidth) {
if ((pWidth & 0x03) != 0) {
return (pWidth & ~0x03) + 4;
}
return pWidth;
}
private void readBitmap16(final BitmapDescriptor pBitmap) throws IOException {
// TODO: No idea if this actually works..
short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
// Will create TYPE_USHORT_555;
DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
DataBuffer buffer = new DataBufferShort(pixels, pixels.length);
WritableRaster raster = Raster.createPackedRaster(
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
);
pBitmap.mImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
for (int y = 0; y < pBitmap.getHeight(); y++) {
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
mImageInput.readFully(pixels, offset, pBitmap.getWidth());
// Skip to 32 bit boundary
if (pBitmap.getWidth() % 2 != 0) {
mImageInput.readShort();
}
if (abortRequested()) {
processReadAborted();
break;
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
}
private void readBitmap24(final BitmapDescriptor pBitmap) throws IOException {
byte[] pixels = new byte[pBitmap.getWidth() * pBitmap.getHeight() * 3];
// Create TYPE_3BYTE_BGR
DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
int[] nBits = {8, 8, 8};
int[] bOffs = {2, 1, 0};
ComponentColorModel cm = new ComponentColorModel(
cs, nBits, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE
);
WritableRaster raster = Raster.createInterleavedRaster(
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), 3, bOffs, null
);
pBitmap.mImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
for (int y = 0; y < pBitmap.getHeight(); y++) {
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
mImageInput.readFully(pixels, offset, pBitmap.getWidth() * 3);
// TODO: Possibly read padding byte here!
if (abortRequested()) {
processReadAborted();
break;
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
}
private void readBitmap32(final BitmapDescriptor pBitmap) throws IOException {
int[] pixels = new int[pBitmap.getWidth() * pBitmap.getHeight()];
// Will create TYPE_INT_ARGB
DirectColorModel cm = (DirectColorModel) ColorModel.getRGBdefault();
DataBuffer buffer = new DataBufferInt(pixels, pixels.length);
WritableRaster raster = Raster.createPackedRaster(
buffer, pBitmap.getWidth(), pBitmap.getHeight(), pBitmap.getWidth(), cm.getMasks(), null
);
pBitmap.mImage = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
for (int y = 0; y < pBitmap.getHeight(); y++) {
int offset = (pBitmap.getHeight() - y - 1) * pBitmap.getWidth();
mImageInput.readFully(pixels, offset, pBitmap.getWidth());
if (abortRequested()) {
processReadAborted();
break;
}
processImageProgress(100 * y / (float) pBitmap.getHeight());
}
}
private Directory getDirectory() throws IOException {
assertInput();
if (mDirectory == null) {
readFileHeader();
}
return mDirectory;
}
private void readFileHeader() throws IOException {
mImageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
// Read file header
mImageInput.readUnsignedShort(); // Reserved
// Should be same as type as the provider
int type = mImageInput.readUnsignedShort();
int imageCount = mImageInput.readUnsignedShort();
// Read directory
mDirectory = Directory.read(type, imageCount, mImageInput);
}
final DirectoryEntry getEntry(final int pImageIndex) throws IOException {
Directory directory = getDirectory();
if (pImageIndex < 0 || pImageIndex >= directory.count()) {
throw new IndexOutOfBoundsException(String.format("Index: %d, ImageCount: %d", pImageIndex, directory.count()));
}
return directory.getEntry(pImageIndex);
}
/// Test code below, ignore.. :-)
public static void main(final String[] pArgs) throws IOException {
if (pArgs.length == 0) {
System.err.println("Please specify the icon file name");
System.exit(1);
}
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e) {
// Ignore
}
String title = new File(pArgs[0]).getName();
JFrame frame = createWindow(title);
JPanel root = new JPanel(new FlowLayout());
JScrollPane scroll =
new JScrollPane(root, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setBorder(BorderFactory.createEmptyBorder());
frame.setContentPane(scroll);
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("ico");
if (!readers.hasNext()) {
System.err.println("No reader for format 'ico' found");
System.exit(1);
}
ImageReader reader = readers.next();
for (String arg : pArgs) {
JPanel panel = new JPanel(null);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
readImagesInFile(arg, reader, panel);
root.add(panel);
}
frame.pack();
frame.setVisible(true);
}
private static void readImagesInFile(String pFileName, ImageReader pReader, final Container pContainer) throws IOException {
File file = new File(pFileName);
if (!file.isFile()) {
System.err.println(pFileName + " not found, or is no file");
}
pReader.setInput(ImageIO.createImageInputStream(file));
int imageCount = pReader.getNumImages(true);
for (int i = 0; i < imageCount; i++) {
try {
addImage(pContainer, pReader, i);
}
catch (Exception e) {
System.err.println("FileName: " + pFileName);
System.err.println("Icon: " + i);
e.printStackTrace();
}
}
}
private static JFrame createWindow(final String pTitle) {
JFrame frame = new JFrame(pTitle);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
System.exit(0);
}
});
return frame;
}
private static void addImage(final Container pParent, final ImageReader pReader, final int pImageNo) throws IOException {
final JButton button = new JButton();
BufferedImage image = pReader.read(pImageNo);
button.setIcon(new ImageIcon(image) {
TexturePaint mTexture;
private void createTexture(final GraphicsConfiguration pGraphicsConfiguration) {
BufferedImage pattern = pGraphicsConfiguration.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();
}
mTexture = new TexturePaint(pattern, new Rectangle(pattern.getWidth(), pattern.getHeight()));
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
if (mTexture == null) {
createTexture(c.getGraphicsConfiguration());
}
Graphics2D gr = (Graphics2D) g;
gr.setPaint(mTexture);
gr.fillRect(x, y, getIconWidth(), getIconHeight());
super.paintIcon(c, g, x, y);
}
});
button.setText("" + image.getWidth() + "x" +
image.getHeight() + ": "
+ ((image.getColorModel() instanceof IndexColorModel) ?
"" + ((IndexColorModel) image.getColorModel()).getMapSize() :
"TrueColor"));
pParent.add(button);
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.
*/
package com.twelvemonkeys.imageio.plugins.ico;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* ICOImageReaderSpi
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ICOImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
*/
public class ICOImageReaderSpi extends ImageReaderSpi {
public ICOImageReaderSpi() {
this(IIOUtil.getProviderInfo(ICOImageReaderSpi.class));
}
private ICOImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[]{"ico", "ICO"},
new String[]{"ico"},
new String[]{
"image/vnd.microsoft.icon", // Official IANA MIME
"image/x-icon", // Common extension MIME
"image/ico" // Unofficial, but common
},
"com.twelvemonkeys.imageio.plugins.ico.ICOImageReader",
STANDARD_INPUT_TYPE,
null,
true, null, null, null, null,
true,
null, null,
null, null
);
}
public boolean canDecodeInput(final Object pSource) throws IOException {
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource, DIB.TYPE_ICO);
}
static boolean canDecode(final ImageInputStream pInput, final int pType) throws IOException {
byte[] signature = new byte[4];
try {
pInput.mark();
pInput.readFully(signature);
int count = pInput.readByte() + (pInput.readByte() << 8);
return (signature[0] == 0x0 && signature[1] == 0x0 && signature[2] == pType
&& signature[3] == 0x0 && count > 0);
}
finally {
pInput.reset();
}
}
public ImageReader createReaderInstance(final Object pExtension) throws IOException {
return new ICOImageReader(this);
}
public String getDescription(final Locale pLocale) {
return "Windows Icon Format (ICO) Reader";
}
}

View File

@@ -0,0 +1,2 @@
com.twelvemonkeys.imageio.plugins.ico.ICOImageReaderSpi
com.twelvemonkeys.imageio.plugins.ico.CURImageReaderSpi

View File

@@ -0,0 +1,103 @@
package com.twelvemonkeys.imageio.plugins.ico;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import javax.imageio.ImageReadParam;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* CURImageReaderTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: CURImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class CURImageReaderTestCase extends ImageReaderAbstractTestCase<CURImageReader> {
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/cur/hand.cur"), new Dimension(32, 32)),
new TestData(getClassLoaderResource("/cur/zoom.cur"), new Dimension(32, 32))
);
}
protected ImageReaderSpi createProvider() {
return new CURImageReaderSpi();
}
@Override
protected CURImageReader createReader() {
return new CURImageReader();
}
protected Class<CURImageReader> getReaderClass() {
return CURImageReader.class;
}
protected List<String> getFormatNames() {
return Arrays.asList("cur");
}
protected List<String> getSuffixes() {
return Arrays.asList("cur");
}
protected List<String> getMIMETypes() {
return Arrays.asList("image/vnd.microsoft.cursor", "image/cursor", "image/x-cursor");
}
private void assertHotSpot(final TestData pTestData, final ImageReadParam pParam, final Point pExpected) throws IOException {
CURImageReader reader = createReader();
reader.setInput(pTestData.getInputStream());
BufferedImage image = reader.read(0, pParam);
Object hotspot = image.getProperty("cursor_hotspot");
if (hotspot == Image.UndefinedProperty) {
hotspot = reader.getHotSpot(0);
}
// Typically never happens, because of weirdness
assertNotNull("Hotspot for cursor not present", hotspot);
// Image weirdness
assertTrue("Hotspot for cursor undefined (java.awt.Image.UndefinedProperty)", Image.UndefinedProperty != hotspot);
assertTrue(String.format("Hotspot not a java.awt.Point: %s", hotspot.getClass()), hotspot instanceof Point);
assertEquals(pExpected, hotspot);
}
public void testHandHotspot() throws IOException {
assertHotSpot(getTestData().get(0), null, new Point(15, 15));
}
public void testZoomHotspot() throws IOException {
assertHotSpot(getTestData().get(1), null, new Point(13, 11));
}
public void testHandHotspotWithParam() throws IOException {
ImageReadParam param = new ImageReadParam();
assertHotSpot(getTestData().get(0), param, new Point(15, 15));
}
public void testHandHotspotExplicitDestination() throws IOException {
CURImageReader reader = createReader();
reader.setInput(getTestData().get(0).getInputStream());
BufferedImage image = reader.read(0);
// Create dest image with same data, except properties...
BufferedImage dest = new BufferedImage(
image.getColorModel(), image.getRaster(), image.getColorModel().isAlphaPremultiplied(), null
);
ImageReadParam param = new ImageReadParam();
param.setDestination(dest);
assertHotSpot(getTestData().get(0), param, new Point(15, 15));
}
// TODO: Test cursor is transparent
}

View File

@@ -0,0 +1,66 @@
package com.twelvemonkeys.imageio.plugins.ico;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/**
* ICOImageReaderTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ICOImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class ICOImageReaderTestCase extends ImageReaderAbstractTestCase<ICOImageReader> {
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(
getClassLoaderResource("/ico/JavaCup.ico"),
new Dimension(48, 48), new Dimension(32, 32), new Dimension(16, 16),
new Dimension(48, 48), new Dimension(32, 32), new Dimension(16, 16),
new Dimension(48, 48), new Dimension(32, 32), new Dimension(16, 16)
),
new TestData(getClassLoaderResource("/ico/favicon.ico"), new Dimension(32, 32)),
new TestData(
getClassLoaderResource("/ico/joypad.ico"),
new Dimension(16, 16), new Dimension(24, 24), new Dimension(32, 32), new Dimension(48, 48),
new Dimension(16, 16), new Dimension(24, 24), new Dimension(32, 32), new Dimension(48, 48)
),
// Windows Vista icon, PNG encoded for 256x256 sizes
new TestData(
getClassLoaderResource("/ico/down.ico"),
new Dimension(16, 16), new Dimension(16, 16), new Dimension(32, 32), new Dimension(32, 32),
new Dimension(48, 48), new Dimension(48, 48), new Dimension(256, 256), new Dimension(256, 256),
new Dimension(16, 16), new Dimension(32, 32), new Dimension(48, 48), new Dimension(256, 256)
)
);
}
protected ImageReaderSpi createProvider() {
return new ICOImageReaderSpi();
}
@Override
protected ICOImageReader createReader() {
return new ICOImageReader();
}
protected Class<ICOImageReader> getReaderClass() {
return ICOImageReader.class;
}
protected List<String> getFormatNames() {
return Arrays.asList("ico");
}
protected List<String> getSuffixes() {
return Arrays.asList("ico");
}
protected List<String> getMIMETypes() {
return Arrays.asList("image/vnd.microsoft.icon", "image/ico", "image/x-icon");
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

1
imageio/imageio-ico/todo.txt Executable file
View File

@@ -0,0 +1 @@
- Support all DIB formats?

25
imageio/imageio-iff/license.txt Executable file
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,32 @@
<?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-iff</artifactId>
<version>2.3-SNAPSHOT</version>
<name>TwelveMonkeys ImageIO IFF plugin</name>
<description>
ImageIO plugin for Amiga/Electronic Arts Interchange Filed Format (IFF)
type ILBM and PBM format.
</description>
<parent>
<artifactId>twelvemonkeys-imageio</artifactId>
<groupId>com.twelvemonkeys</groupId>
<version>2.3-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,180 @@
/*
* 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.plugins.iff;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* BMHDChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BMHDChunk.java,v 1.0 28.feb.2006 00:04:32 haku Exp$
*/
class BMHDChunk extends IFFChunk {
//
// typedef UBYTE Masking; /* Choice of masking technique. */
//
// #define mskNone 0
// #define mskHasMask 1
// #define mskHasTransparentColor 2
// #define mskLasso 3
//
// typedef UBYTE Compression; /* Choice of compression algorithm
// applied to the rows of all source and mask planes. "cmpByteRun1"
// is the byte run encoding described in Appendix C. Do not compress
// across rows! */
// #define cmpNone 0
// #define cmpByteRun1 1
//
// typedef struct {
// UWORD w, h; /* raster width & height in pixels */
// WORD x, y; /* pixel position for this image */
// UBYTE nPlanes; /* # source bitplanes */
// Masking masking;
// Compression compression;
// UBYTE pad1; /* unused; ignore on read, write as 0 */
// UWORD transparentColor; /* transparent "color number" (sort of) */
// UBYTE xAspect, yAspect; /* pixel aspect, a ratio width : height */
// WORD pageWidth, pageHeight; /* source "page" size in pixels */
// } BitMapHeader;*/
static final int MASK_NONE = 0;
static final int MASK_HAS_MASK = 1;
static final int MASK_TRANSPARENT_COLOR = 2;
static final int MASK_LASSO = 3;
static final int COMPRESSION_NONE = 0;
// RLE
static final int COMPRESSION_BYTE_RUN = 1;
// NOTE: Each row of the image is stored in an integral number of 16 bit
// words. The number of words per row is words=((w+15)/16)
// Dimensions of raster
int mWidth;
int mHeight;
// Source offsets
// Hmm.. Consider making these Image.properties?
int mXPos;
int mYPos;
// The number of source bitplanes in the BODY chunk (see below) is stored in
// nPlanes. An ILBM with a CMAP but no BODY and nPlanes = 0 is the
// recommended way to store a color map.
int mBitplanes;
int mMaskType;
int mCompressionType;
int mTransparentIndex;
// NOTE: Typical values are 10:11 (320 x 200)
int mXAspect;
int mYAspect;
// Source page dimension
// NOTE: The image may be larger than the page, probably ignore these
int mPageWidth;
int mPageHeight;
protected BMHDChunk(int pChunkLength) {
super(IFF.CHUNK_BMHD, pChunkLength);
}
protected BMHDChunk(int pWidth, int pHeight, int pBitplanes,
int pMaskType, int pCompressionType,
int pTransparentIndex) {
super(IFF.CHUNK_BMHD, 20);
mWidth = pWidth;
mHeight = pHeight;
mXPos = 0;
mYPos = 0;
mBitplanes = pBitplanes;
mMaskType = pMaskType;
mCompressionType = pCompressionType;
mTransparentIndex = pTransparentIndex;
mXAspect = 1;
mYAspect = 1;
mPageWidth = Math.min(pWidth, Short.MAX_VALUE); // For some reason, these are signed?
mPageHeight = Math.min(pHeight, Short.MAX_VALUE);
}
void readChunk(DataInput pInput) throws IOException {
if (mChunkLength != 20) {
throw new IIOException("Unknown BMHD chunk length: " + mChunkLength);
}
mWidth = pInput.readUnsignedShort();
mHeight = pInput.readUnsignedShort();
mXPos = pInput.readShort();
mYPos = pInput.readShort();
mBitplanes = pInput.readUnsignedByte();
mMaskType = pInput.readUnsignedByte();
mCompressionType = pInput.readUnsignedByte();
pInput.readByte(); // PAD
mTransparentIndex = pInput.readUnsignedShort();
mXAspect = pInput.readUnsignedByte();
mYAspect = pInput.readUnsignedByte();
mPageWidth = pInput.readShort();
mPageHeight = pInput.readShort();
}
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeInt(mChunkId);
pOutput.writeInt(mChunkLength);
pOutput.writeShort(mWidth);
pOutput.writeShort(mHeight);
pOutput.writeShort(mXPos);
pOutput.writeShort(mYPos);
pOutput.writeByte(mBitplanes);
pOutput.writeByte(mMaskType);
pOutput.writeByte(mCompressionType);
pOutput.writeByte(0); // PAD
pOutput.writeShort(mTransparentIndex);
pOutput.writeByte(mXAspect);
pOutput.writeByte(mYAspect);
pOutput.writeShort(mPageWidth);
pOutput.writeShort(mPageHeight);
}
public String toString() {
return super.toString()
+ " {w=" + mWidth + ", h=" + mHeight
+ ", x=" + mXPos + ", y=" + mYPos
+ ", planes=" + mBitplanes + ", mask=" + mMaskType
+ ", compression=" + mCompressionType + ", trans=" + mTransparentIndex
+ ", xAspect=" + mXAspect + ", yAspect=" + mYAspect
+ ", pageWidth=" + mPageWidth + ", pageHeight=" + mPageHeight + "}";
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.plugins.iff;
import java.io.DataInput;
import java.io.IOException;
import java.io.DataOutput;
/**
* BODYChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BODYChunk.java,v 1.0 28.feb.2006 01:25:49 haku Exp$
*/
class BODYChunk extends IFFChunk {
protected BODYChunk(int pChunkLength) {
super(IFF.CHUNK_BODY, pChunkLength);
}
void readChunk(DataInput pInput) throws IOException {
throw new InternalError("BODY chunk should only be read from IFFImageReader");
}
void writeChunk(DataOutput pOutput) throws IOException {
throw new InternalError("BODY chunk should only be written from IFFImageWriter");
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.plugins.iff;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* CAMGChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CAMGChunk.java,v 1.0 28.feb.2006 02:10:07 haku Exp$
*/
class CAMGChunk extends IFFChunk {
// HIRES=0x8000, LACE=0x4
// #define CAMG_HAM 0x800 /* hold and modify */
// #define CAMG_EHB 0x80 /* extra halfbrite */
private int mCAMG;
public CAMGChunk(int pLength) {
super(IFF.CHUNK_CAMG, pLength);
}
void readChunk(DataInput pInput) throws IOException {
if (mChunkLength != 4) {
throw new IIOException("Unknown CAMG chunk length: " + mChunkLength);
}
mCAMG = pInput.readInt();
}
void writeChunk(DataOutput pOutput) throws IOException {
throw new InternalError("Not implemented: writeChunk()");
}
boolean isHAM() {
return (mCAMG & 0x800) != 0;
}
boolean isEHB() {
return (mCAMG & 0x80) != 0;
}
public String toString() {
return super.toString() + " {mode=" + (isHAM() ? "HAM" : isEHB() ? "EHB" : "Normal") + "}";
}
}

View File

@@ -0,0 +1,170 @@
/*
* 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.plugins.iff;
import com.twelvemonkeys.image.InverseColorMapIndexColorModel;
import javax.imageio.IIOException;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* CMAPChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CMAPChunk.java,v 1.0 28.feb.2006 00:38:05 haku Exp$
*/
class CMAPChunk extends IFFChunk {
// typedef struct {
// UBYTE red, green, blue; /* color intensities 0..255 */
// } ColorRegister; /* size = 3 bytes */
//
// typedef ColorRegister ColorMap[n]; /* size = 3n bytes */
byte[] mReds;
byte[] mGreens;
byte[] mBlues;
boolean mEHB;
final private BMHDChunk mHeader;
final private CAMGChunk mCamg;
private IndexColorModel mModel;
protected CMAPChunk(int pChunkLength, BMHDChunk pHeader, CAMGChunk pCamg) {
super(IFF.CHUNK_CMAP, pChunkLength);
mHeader = pHeader;
mCamg = pCamg;
}
public CMAPChunk(IndexColorModel pModel) {
super(IFF.CHUNK_CMAP, pModel.getMapSize() * 3);
mModel = pModel;
mHeader = null;
mCamg = null;
}
void readChunk(DataInput pInput) throws IOException {
int numColors = mChunkLength / 3;
int paletteSize = numColors;
boolean isEHB = mCamg != null && mCamg.isEHB();
if (isEHB) {
if (numColors == 32) {
paletteSize = 64;
}
else {
throw new IIOException("Unknown number of colors for EHB: " + numColors);
}
}
mReds = new byte[paletteSize];
mGreens = mReds.clone();
mBlues = mReds.clone();
for (int i = 0; i < numColors; i++) {
mReds[i] = pInput.readByte();
mGreens[i] = pInput.readByte();
mBlues[i] = pInput.readByte();
}
if (isEHB) {
for (int i = 0; i < numColors; i++) {
mReds[i + numColors] = (byte) ((mReds[i] & 0xff) / 2);
mGreens[i + numColors] = (byte) ((mGreens[i] & 0xff) / 2);
mBlues[i + numColors] = (byte) ((mBlues[i] & 0xff) / 2);
}
}
// TODO: When reading in a CMAP for 8-bit-per-gun display or
// manipulation, you may want to assume that any CMAP which has 0 values
// for the low bits of all guns for all registers was stored shifted
// rather than scaled, and provide your own scaling.
// Use defaults if the color map is absent or has fewer color registers
// than you need. Ignore any extra color registers.
// R8 := (Rn x 255 ) / maxColor
// All chunks are WORD aligned (even sized), may need to read pad...
if (mChunkLength % 2 != 0) {
pInput.readByte();
}
// TODO: Bitmask transparency
// Would it work to double to numbers of colors, and create an indexcolormodel,
// with alpha, where all colors above the original color is all transparent?
// This is a waste of time and space, of course...
int trans = mHeader.mMaskType == BMHDChunk.MASK_TRANSPARENT_COLOR ? mHeader.mTransparentIndex : -1;
mModel = new InverseColorMapIndexColorModel(mHeader.mBitplanes, mReds.length, mReds, mGreens, mBlues, trans);
}
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeInt(mChunkId);
pOutput.writeInt(mChunkLength);
final int length = mModel.getMapSize();
for (int i = 0; i < length; i++) {
pOutput.writeByte(mModel.getRed(i));
pOutput.writeByte(mModel.getGreen(i));
pOutput.writeByte(mModel.getBlue(i));
}
if (mChunkLength % 2 != 0) {
pOutput.writeByte(0); // PAD
}
}
public String toString() {
return super.toString() + " {colorMap=" + mModel + "}";
}
IndexColorModel getIndexColorModel() {
return mModel;
}
public BufferedImage createPaletteImage() {
// Create a 1 x colors.length image
IndexColorModel cm = getIndexColorModel();
WritableRaster raster = cm.createCompatibleWritableRaster(cm.getMapSize(), 1);
byte[] pixel = null;
for (int x = 0; x < cm.getMapSize(); x++) {
pixel = (byte[]) cm.getDataElements(cm.getRGB(x), pixel);
raster.setDataElements(x, 0, pixel);
}
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.plugins.iff;
import javax.imageio.IIOException;
import java.awt.*;
import java.awt.geom.Point2D;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* GRABChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: GRABChunk.java,v 1.0 28.feb.2006 01:55:05 haku Exp$
*/
class GRABChunk extends IFFChunk {
// typedef struct {
// WORD x, y; /* relative coordinates (pixels) */
// } Point2D;
Point2D mPoint;
protected GRABChunk(int pChunkLength) {
super(IFF.CHUNK_GRAB, pChunkLength);
}
protected GRABChunk(Point2D pPoint) {
super(IFF.CHUNK_GRAB, 4);
mPoint = pPoint;
}
void readChunk(DataInput pInput) throws IOException {
if (mChunkLength != 4) {
throw new IIOException("Unknown GRAB chunk size: " + mChunkLength);
}
mPoint = new Point(pInput.readShort(), pInput.readShort());
}
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeShort((int) mPoint.getX());
pOutput.writeShort((int) mPoint.getY());
}
public String toString() {
return super.toString() + " {point=" + mPoint + "}";
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.plugins.iff;
import java.io.DataInput;
import java.io.IOException;
import java.io.DataOutput;
/**
* UnknownChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: UnknownChunk.java,v 1.0 28.feb.2006 00:53:47 haku Exp$
*/
class GenericChunk extends IFFChunk {
byte[] mData;
protected GenericChunk(int pChunkId, int pChunkLength) {
super(pChunkId, pChunkLength);
mData = new byte[pChunkLength <= 50 ? pChunkLength : 47];
}
protected GenericChunk(int pChunkId, byte[] pChunkData) {
super(pChunkId, pChunkData.length);
mData = pChunkData;
}
void readChunk(DataInput pInput) throws IOException {
pInput.readFully(mData, 0, mData.length);
int toSkip = mChunkLength - mData.length;
while (toSkip > 0) {
toSkip -= pInput.skipBytes(toSkip);
}
// Read pad
if (mChunkLength % 2 != 0) {
pInput.readByte();
}
}
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeInt(mChunkId);
pOutput.writeInt(mChunkLength);
pOutput.write(mData, 0, mData.length);
if (mData.length % 2 != 0) {
pOutput.writeByte(0); // PAD
}
}
public String toString() {
return super.toString() + " {value=\""
+ new String(mData, 0, mData.length <= 50 ? mData.length : 47)
+ (mChunkLength <= 50 ? "" : "...") + "\"}";
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.plugins.iff;
/**
* IFF format constants.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFF.java,v 1.0 07.mar.2006 15:31:48 haku Exp$
*/
interface IFF {
/** IFF FORM group chunk */
int CHUNK_FORM = ('F' << 24) + ('O' << 16) + ('R' << 8) + 'M';
/** IFF ILBM form type */
int TYPE_ILBM = ('I' << 24) + ('L' << 16) + ('B' << 8) + 'M';
/** IFF PBM form type */
int TYPE_PBM = ('P' << 24) + ('B' << 16) + ('M' << 8) + ' ';
/** Bitmap Header chunk */
int CHUNK_BMHD = ('B' << 24) + ('M' << 16) + ('H' << 8) + 'D';
/** Color map chunk */
int CHUNK_CMAP = ('C' << 24) + ('M' << 16) + ('A' << 8) + 'P';
/** Hotspot chunk (cursors, brushes) */
int CHUNK_GRAB = ('G' << 24) + ('R' << 16) + ('A' << 8) + 'B';
/** Destination merge data chunk */
int CHUNK_DEST = ('D' << 24) + ('E' << 16) + ('S' << 8) + 'T';
/** Sprite information chunk */
int CHUNK_SPRT = ('S' << 24) + ('P' << 16) + ('R' << 8) + 'T';
/** Commodore Amiga viewport mode chunk (used to determine HAM and EHB modes) */
int CHUNK_CAMG = ('C' << 24) + ('A' << 16) + ('M' << 8) + 'G';
/** Main data (body) chunk */
int CHUNK_BODY = ('B' << 24) + ('O' << 16) + ('D' << 8) + 'Y';
}

View File

@@ -0,0 +1,58 @@
/*
* 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.plugins.iff;
import java.io.DataInput;
import java.io.IOException;
import java.io.DataOutput;
/**
* IFFChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFFChunk.java,v 1.0 28.feb.2006 00:00:45 haku Exp$
*/
abstract class IFFChunk {
int mChunkId;
int mChunkLength;
protected IFFChunk(int pChunkId, int pChunkLength) {
mChunkId = pChunkId;
mChunkLength = pChunkLength;
}
abstract void readChunk(DataInput pInput) throws IOException;
abstract void writeChunk(DataOutput pOutput) throws IOException;
public String toString() {
return IFFUtil.toChunkStr(mChunkId) + " chunk (" + mChunkLength + " bytes)";
}
}

View File

@@ -0,0 +1,716 @@
/*
* 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.plugins.iff;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.PackBitsDecoder;
import javax.imageio.*;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Reader for Amiga (Electronic Arts) IFF ILBM (InterLeaved BitMap) and PBM
* format (Packed BitMap).
* The IFF format (Interchange File Format) is the standard file format
* supported by allmost all image software for the Amiga computer.
* <p/>
* This reader supports the original palette-based 1-8 bit formats, including
* EHB (Extra Halfbright), HAM (Hold and Modify), and the more recent "deep"
* formats, 8 bit gray, 24 bit RGB and 32 bit ARGB.
* Uncompressed and ByteRun1 compressed (run lenght encoding) files are
* supported.
* <p/>
* Palette based images are read as {@code BufferedImage} of
* {@link BufferedImage#TYPE_BYTE_INDEXED TYPE_BYTE_INDEXED} or
* {@link BufferedImage#TYPE_BYTE_BINARY BufferedImage#}
* depending on the bit depth.
* Gray images are read as
* {@link BufferedImage#TYPE_BYTE_GRAY TYPE_BYTE_GRAY}.
* 24 bit true-color images are read as
* {@link BufferedImage#TYPE_3BYTE_BGR TYPE_3BYTE_BGR}.
* 32 bit true-color images are read as
* {@link BufferedImage#TYPE_4BYTE_ABGR TYPE_4BYTE_ABGR}.
* <p/>
* Issues: HAM and HAM8 (Hold and Modify) formats are converted to RGB (24 bit),
* as it seems to be very hard to create an {@code IndexColorModel} subclass
* that would correctly describe these formats.
* These formats utilizes the special display hardware in the Amiga computers.
* HAM (6 bits) needs 12 bits storage/pixel, if unpacked to RGB (4 bits/gun).
* HAM8 (8 bits) needs 18 bits storage/pixel, if unpacked to RGB (6 bits/gun).
* See <a href="http://en.wikipedia.org/wiki/Hold_And_Modify">Wikipedia: HAM</a>
* for more information.
* <br/>
* EHB palette is expanded to an {@link IndexColorModel} with 64 entries.
* See <a href="http://en.wikipedia.org/wiki/Extra_Half-Brite">Wikipedia: EHB</a>
* for more information.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: IFFImageReader.java,v 1.0 29.aug.2004 20:26:58 haku Exp $
* @see <a href="http://en.wikipedia.org/wiki/Interchange_File_Format">Wikipedia: IFF</a>
* @see <a href="http://en.wikipedia.org/wiki/ILBM">Wikipedia: IFF ILBM</a>
*/
public class IFFImageReader extends ImageReaderBase {
// http://home.comcast.net/~erniew/lwsdk/docs/filefmts/ilbm.html
// http://www.fileformat.info/format/iff/spec/7866a9f0e53c42309af667c5da3bd426/view.htm
// - Contains definitions of some "new" chunks, as well as alternative FORM types
// TODO: One other existing deep bit ordering that you may encounter is the 21-bit
// NewTek format.
//
// NewTek deep ILBM bit ordering:
// saved first ------------------------------------------------------> saved last
// R7 G7 B7 R6 G6 B6 R5 G5 B5 R4 G4 B4 R3 G3 B3 R2 G2 B2 R1 G1 B1 R0 G0 B0
private BMHDChunk mHeader;
private CMAPChunk mColorMap;
private BODYChunk mBody;
private GRABChunk mGrab;
private CAMGChunk mViewPort;
private int mFormType;
private long mBodyStart;
private BufferedImage mImage;
private DataInputStream mByteRunStream;
public IFFImageReader() {
super(IFFImageReaderSpi.sharedProvider());
}
protected IFFImageReader(ImageReaderSpi pProvider) {
super(pProvider);
}
private void init(int pIndex) throws IOException {
checkBounds(pIndex);
if (mHeader == null) {
readMeta();
}
}
protected void resetMembers() {
mHeader = null;
mColorMap = null;
mBody = null;
mViewPort = null;
mFormType = 0;
mImage = null;
mByteRunStream = null;
}
private void readMeta() throws IOException {
if (mImageInput.readInt() != IFF.CHUNK_FORM) {
throw new IIOException("Unknown file format for IFFImageReader");
}
int remaining = mImageInput.readInt() - 4; // We'll read 4 more in a sec
mFormType = mImageInput.readInt();
if (mFormType != IFF.TYPE_ILBM && mFormType != IFF.TYPE_PBM) {
throw new IIOException("Only IFF (FORM) type ILBM and PBM supported: " + IFFUtil.toChunkStr(mFormType));
}
//System.out.println("IFF type FORM " + toChunkStr(type));
mGrab = null;
mViewPort = null;
while (remaining > 0) {
int chunkId = mImageInput.readInt();
int length = mImageInput.readInt();
remaining -= 8;
remaining -= length % 2 == 0 ? length : length + 1;
//System.out.println("Next chunk: " + toChunkStr(chunkId) + " length: " + length);
//System.out.println("Remaining bytes after chunk: " + remaining);
switch (chunkId) {
case IFF.CHUNK_BMHD:
if (mHeader != null) {
throw new IIOException("Multiple BMHD chunks not allowed");
}
mHeader = new BMHDChunk(length);
mHeader.readChunk(mImageInput);
//System.out.println(mHeader);
break;
case IFF.CHUNK_CMAP:
if (mColorMap != null) {
throw new IIOException("Multiple CMAP chunks not allowed");
}
mColorMap = new CMAPChunk(length, mHeader, mViewPort);
mColorMap.readChunk(mImageInput);
//System.out.println(mColorMap);
break;
case IFF.CHUNK_GRAB:
if (mGrab != null) {
throw new IIOException("Multiple GRAB chunks not allowed");
}
mGrab = new GRABChunk(length);
mGrab.readChunk(mImageInput);
//System.out.println(mGrab);
break;
case IFF.CHUNK_CAMG:
if (mViewPort != null) {
throw new IIOException("Multiple CAMG chunks not allowed");
}
mViewPort = new CAMGChunk(length);
mViewPort.readChunk(mImageInput);
//System.out.println(mViewPort);
break;
case IFF.CHUNK_BODY:
if (mBody != null) {
throw new IIOException("Multiple BODY chunks not allowed");
}
mBody = new BODYChunk(length);
mBodyStart = mImageInput.getStreamPosition();
// NOTE: We don't read the body here, it's done later in the read(int, ImageReadParam) method
// Done reading meta
return;
default:
// TODO: We probably want to store anno chunks as Metadata
// ANNO, DEST, SPRT and more
IFFChunk generic = new GenericChunk(chunkId, length);
generic.readChunk(mImageInput);
//System.out.println(generic);
break;
}
}
}
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
init(pIndex);
processImageStarted(pIndex);
mImage = getDestination(pParam, getImageTypes(pIndex), mHeader.mWidth, mHeader.mHeight);
//System.out.println(mBody);
if (mBody != null) {
//System.out.println("Read body");
readBody(pParam);
}
else {
// TODO: Remove this hack when we have metadata
// In the rare case of an ILBM containing nothing but a CMAP
//System.out.println(mColorMap);
if (mColorMap != null) {
//System.out.println("Creating palette!");
mImage = mColorMap.createPaletteImage();
}
}
BufferedImage result = mImage;
processImageComplete();
return result;
}
public int getWidth(int pIndex) throws IOException {
init(pIndex);
return mHeader.mWidth;
}
public int getHeight(int pIndex) throws IOException {
init(pIndex);
return mHeader.mHeight;
}
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) throws IOException {
init(pIndex);
List<ImageTypeSpecifier> types = Arrays.asList(
getRawImageType(pIndex),
ImageTypeSpecifier.createFromBufferedImageType(mHeader.mBitplanes == 32 ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR)
// TODO: ImageTypeSpecifier.createFromBufferedImageType(mHeader.mBitplanes == 32 ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB),
// TODO: Allow 32 bit always. Allow RGB and discard alpha, if present?
);
return types.iterator();
}
@Override
public ImageTypeSpecifier getRawImageType(int pIndex) throws IOException {
init(pIndex);
// TODO: Stay DRY...
// TODO: Use this for creating the Image/Buffer in the read code below...
// NOTE: mColorMap may be null for 8 bit (gray), 24 bit or 32 bit only
ImageTypeSpecifier specifier;
switch (mHeader.mBitplanes) {
case 1:
// 1 bit
case 2:
// 2 bit
case 3:
case 4:
// 4 bit
case 5:
case 6:
// May be HAM6
// May be EHB
case 7:
case 8:
// 8 bit
// May be HAM8
if (!isHAM()) {
if (mColorMap != null) {
IndexColorModel cm = mColorMap.getIndexColorModel();
specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
break;
}
else {
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
break;
}
}
// NOTE: HAM modes falls through, as they are converted to RGB
case 24:
// 24 bit RGB
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
break;
case 32:
// 32 bit ARGB
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
break;
default:
throw new IIOException(String.format("Bit depth not implemented: %d", mHeader.mBitplanes));
}
return specifier;
}
private void readBody(final ImageReadParam pParam) throws IOException {
mImageInput.seek(mBodyStart);
mByteRunStream = null;
// NOTE: mColorMap may be null for 8 bit (gray), 24 bit or 32 bit only
if (mColorMap != null) {
IndexColorModel cm = mColorMap.getIndexColorModel();
readIndexed(pParam, mImageInput, cm);
}
else {
readTrueColor(pParam, mImageInput);
}
}
private void readIndexed(final ImageReadParam pParam, final ImageInputStream pInput, final IndexColorModel pModel) throws IOException {
final int width = mHeader.mWidth;
final int height = mHeader.mHeight;
final Rectangle aoi = getSourceRegion(pParam, width, height);
final Point offset = pParam == null ? new Point(0, 0) : pParam.getDestinationOffset();
// Set everything to default values
int sourceXSubsampling = 1;
int sourceYSubsampling = 1;
int[] sourceBands = null;
int[] destinationBands = null;
// Get values from the ImageReadParam, if any
if (pParam != null) {
sourceXSubsampling = pParam.getSourceXSubsampling();
sourceYSubsampling = pParam.getSourceYSubsampling();
sourceBands = pParam.getSourceBands();
destinationBands = pParam.getDestinationBands();
}
// Ensure band settings from param are compatible with images
checkReadParamBandSettings(pParam, isHAM() ? 3 : 1, mImage.getSampleModel().getNumBands());
WritableRaster destination = mImage.getRaster();
if (destinationBands != null || offset.x != 0 || offset.y != 0) {
destination = destination.createWritableChild(0, 0, destination.getWidth(), destination.getHeight(), offset.x, offset.y, destinationBands);
}
// NOTE: Each row of the image is stored in an integral number of 16 bit words.
// The number of words per row is words=((w+15)/16)
int planeWidth = 2 * ((width + 15) / 16);
final byte[] planeData = new byte[8 * planeWidth];
ColorModel cm;
WritableRaster raster;
if (isHAM()) {
// TODO: If HAM6, use type USHORT_444_RGB or 2BYTE_444_RGB?
// Or create a HAMColorModel, if at all possible?
// TYPE_3BYTE_BGR
cm = new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{8, 8, 8},
false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE
);
// Create a byte raster with BGR order
raster = Raster.createInterleavedRaster(
DataBuffer.TYPE_BYTE, width, 1, width * 3, 3, new int[]{2, 1, 0}, null
);
}
else {
// TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED
cm = pModel;
raster = pModel.createCompatibleWritableRaster(width, 1);
}
Raster sourceRow = raster.createChild(aoi.x, 0, aoi.width, 1, 0, 0, sourceBands);
final byte[] row = new byte[width * 8];
//System.out.println("Data length: " + data.length);
//System.out.println("PlaneData length: " + planeData.length * planeData[0].length);
//System.out.println("Row length: " + row.length);
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
final int planes = mHeader.mBitplanes;
Object dataElements = null;
Object outDataElements = null;
ColorConvertOp converter = null;
for (int srcY = 0; srcY < height; srcY++) {
for (int p = 0; p < planes; p++) {
readPlaneData(pInput, planeData, p * planeWidth, planeWidth);
}
// Skip rows outside AOI
if (srcY < aoi.y || (srcY - aoi.y) % sourceYSubsampling != 0) {
continue;
}
else if (srcY >= (aoi.y + aoi.height)) {
return;
}
if (mFormType == IFF.TYPE_ILBM) {
int pixelPos = 0;
for (int planePos = 0; planePos < planeWidth; planePos++) {
IFFUtil.bitRotateCW(planeData, planePos, planeWidth, row, pixelPos, 1);
pixelPos += 8;
}
if (isHAM()) {
hamToRGB(row, pModel, data, 0);
}
else {
raster.setDataElements(0, 0, width, 1, row);
}
}
else if (mFormType == IFF.TYPE_PBM) {
// TODO: Arraycopy might not be necessary, if it's okay with row larger than width
System.arraycopy(planeData, 0, row, 0, mHeader.mBitplanes * planeWidth);
raster.setDataElements(0, 0, width, 1, row);
}
else {
throw new AssertionError(String.format("Unsupported FORM type: %s", mFormType));
}
int dstY = (srcY - aoi.y) / sourceYSubsampling;
// Handle non-converting raster as special case for performance
if (cm.isCompatibleRaster(destination)) {
// Rasters are compatible, just write to destinaiton
if (sourceXSubsampling == 1) {
destination.setRect(offset.x, dstY, sourceRow);
// dataElements = raster.getDataElements(aoi.x, 0, aoi.width, 1, dataElements);
// destination.setDataElements(offset.x, offset.y + (srcY - aoi.y) / sourceYSubsampling, aoi.width, 1, dataElements);
}
else {
for (int srcX = 0; srcX < sourceRow.getWidth(); srcX += sourceXSubsampling) {
dataElements = sourceRow.getDataElements(srcX, 0, dataElements);
int dstX = /*offset.x +*/ srcX / sourceXSubsampling;
destination.setDataElements(dstX, dstY, dataElements);
}
}
}
else {
if (cm instanceof IndexColorModel) {
// TODO: Optimize this thing... Maybe it's faster to just get the data indexed, and use drawImage?
IndexColorModel icm = (IndexColorModel) cm;
for (int srcX = 0; srcX < sourceRow.getWidth(); srcX += sourceXSubsampling) {
dataElements = sourceRow.getDataElements(srcX, 0, dataElements);
int rgb = icm.getRGB(dataElements);
outDataElements = mImage.getColorModel().getDataElements(rgb, outDataElements);
int dstX = srcX / sourceXSubsampling;
destination.setDataElements(dstX, dstY, outDataElements);
}
}
else {
// TODO: This branch is never tested, and is probably "dead"
// ColorConvertOp
if (converter == null) {
converter = new ColorConvertOp(cm.getColorSpace(), mImage.getColorModel().getColorSpace(), null);
}
converter.filter(
raster.createChild(aoi.x, 0, aoi.width, 1, 0, 0, null),
destination.createWritableChild(offset.x, offset.y + srcY - aoi.y, aoi.width, 1, 0, 0, null)
);
}
}
processImageProgress(srcY * 100f / mHeader.mWidth);
if (abortRequested()) {
processReadAborted();
break;
}
}
}
// One row from each of the 24 bitplanes is written before moving to the
// next scanline. For each scanline, the red bitplane rows are stored first,
// followed by green and blue. The first plane holds the least significant
// bit of the red value for each pixel, and the last holds the most
// significant bit of the blue value.
private void readTrueColor(ImageReadParam pParam, final ImageInputStream pInput) throws IOException {
final int width = mHeader.mWidth;
final int height = mHeader.mHeight;
final Rectangle aoi = getSourceRegion(pParam, width, height);
final Point offset = pParam == null ? new Point(0, 0) : pParam.getDestinationOffset();
// Set everything to default values
int sourceXSubsampling = 1;
int sourceYSubsampling = 1;
int[] sourceBands = null;
int[] destinationBands = null;
// Get values from the ImageReadParam, if any
if (pParam != null) {
sourceXSubsampling = pParam.getSourceXSubsampling();
sourceYSubsampling = pParam.getSourceYSubsampling();
sourceBands = pParam.getSourceBands();
destinationBands = pParam.getDestinationBands();
}
// Ensure band settings from param are compatible with images
checkReadParamBandSettings(pParam, mHeader.mBitplanes / 8, mImage.getSampleModel().getNumBands());
// NOTE: Each row of the image is stored in an integral number of 16 bit words.
// The number of words per row is words=((w+15)/16)
int planeWidth = 2 * ((width + 15) / 16);
final byte[] planeData = new byte[8 * planeWidth];
WritableRaster destination = mImage.getRaster();
if (destinationBands != null || offset.x != 0 || offset.y != 0) {
destination = destination.createWritableChild(0, 0, destination.getWidth(), destination.getHeight(), offset.x, offset.y, destinationBands);
}
// WritableRaster raster = mImage.getRaster().createCompatibleWritableRaster(width, 1);
WritableRaster raster = mImage.getRaster().createCompatibleWritableRaster(8 * planeWidth, 1);
Raster sourceRow = raster.createChild(aoi.x, 0, aoi.width, 1, 0, 0, sourceBands);
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
final int channels = (mHeader.mBitplanes + 7) / 8;
final int planesPerChannel = 8;
Object dataElements = null;
for (int srcY = 0; srcY < height; srcY++) {
for (int c = 0; c < channels; c++) {
for (int p = 0; p < planesPerChannel; p++) {
readPlaneData(pInput, planeData, p * planeWidth, planeWidth);
}
// Skip rows outside AOI
if (srcY < aoi.y || (srcY - aoi.y) % sourceYSubsampling != 0) {
continue;
}
else if (srcY >= (aoi.y + aoi.height)) {
return;
}
if (mFormType == IFF.TYPE_ILBM) {
// NOTE: Using (channels - c - 1) instead of just c,
// effectively reverses the channel order from RGBA to ABGR
int off = (channels - c - 1);
int pixelPos = 0;
for (int planePos = 0; planePos < planeWidth; planePos++) {
IFFUtil.bitRotateCW(planeData, planePos, planeWidth, data, off + pixelPos * channels, channels);
pixelPos += 8;
}
}
else if (mFormType == IFF.TYPE_PBM) {
System.arraycopy(planeData, 0, data, srcY * 8 * planeWidth, planeWidth);
}
else {
throw new AssertionError(String.format("Unsupported FORM type: %s", mFormType));
}
}
int dstY = (srcY - aoi.y) / sourceYSubsampling;
// TODO: Support conversion to INT (A)RGB rasters (maybe using ColorConvertOp?)
// TODO: Avoid createChild if no region?
if (sourceXSubsampling == 1) {
destination.setRect(0, dstY, sourceRow);
// dataElements = raster.getDataElements(aoi.x, 0, aoi.width, 1, dataElements);
// destination.setDataElements(offset.x, offset.y + (srcY - aoi.y) / sourceYSubsampling, aoi.width, 1, dataElements);
}
else {
for (int srcX = 0; srcX < sourceRow.getWidth(); srcX += sourceXSubsampling) {
dataElements = sourceRow.getDataElements(srcX, 0, dataElements);
int dstX = srcX / sourceXSubsampling;
destination.setDataElements(dstX, dstY, dataElements);
}
}
processImageProgress(srcY * 100f / mHeader.mWidth);
if (abortRequested()) {
processReadAborted();
break;
}
}
}
private void readPlaneData(final ImageInputStream pInput, final byte[] pData, final int pOffset, final int pPlaneWidth)
throws IOException {
switch (mHeader.mCompressionType) {
case BMHDChunk.COMPRESSION_NONE:
pInput.readFully(pData, pOffset, pPlaneWidth);
// Uncompressed rows must have even number of bytes
if ((mHeader.mBitplanes * pPlaneWidth) % 2 != 0) {
pInput.readByte();
}
break;
case BMHDChunk.COMPRESSION_BYTE_RUN:
// TODO: How do we know if the last byte in the body is a pad byte or not?!
// The body consists of byte-run (PackBits) compressed rows of bit plane data.
// However, we don't know how long each compressed row is, without decoding it...
// The workaround below, is to use a decode buffer size of pPlaneWidth,
// to make sure we don't decode anything we don't have to (shouldn't).
if (mByteRunStream == null) {
mByteRunStream = new DataInputStream(
new DecoderStream(
IIOUtil.createStreamAdapter(pInput, mBody.mChunkLength),
new PackBitsDecoder(true),
pPlaneWidth * mHeader.mBitplanes
)
);
}
mByteRunStream.readFully(pData, pOffset, pPlaneWidth);
break;
default:
throw new IIOException(String.format("Unknown compression type: %d", mHeader.mCompressionType));
}
}
private void hamToRGB(final byte[] pIndexed, final IndexColorModel pModel,
final byte[] pDest, final int pDestOffset) {
final int bits = mHeader.mBitplanes;
final int width = mHeader.mWidth;
int lastRed = 0;
int lastGreen = 0;
int lastBlue = 0;
for (int x = 0; x < width; x++) {
int pixel = pIndexed[x] & 0xff;
//System.out.println("--> ham" + bits);
int paletteIndex = bits == 6 ? pixel & 0x0f : pixel & 0x3f;
int indexShift = bits == 6 ? 4 : 2;
int colorMask = bits == 6 ? 0x0f : 0x03;
//System.out.println("palette index=" + paletteIndex);
// Get Hold and Modify bits
switch ((pixel >> (8 - indexShift)) & 0x03) {
case 0x00:// HOLD
lastRed = pModel.getRed(paletteIndex);
lastGreen = pModel.getGreen(paletteIndex);
lastBlue = pModel.getBlue(paletteIndex);
break;
case 0x01:// MODIFY BLUE
lastBlue = (lastBlue & colorMask) | (paletteIndex << indexShift);
break;
case 0x02:// MODIFY RED
lastRed = (lastRed & colorMask) | (paletteIndex << indexShift);
break;
case 0x03:// MODIFY GREEN
lastGreen = (lastGreen & colorMask) | (paletteIndex << indexShift);
break;
}
int offset = (x * 3) + pDestOffset;
pDest[2 + offset] = (byte) lastRed;
pDest[1 + offset] = (byte) lastGreen;
pDest[offset] = (byte) lastBlue;
}
}
private boolean isHAM() {
return mViewPort != null && mViewPort.isHAM();
}
public static void main(String[] pArgs) throws IOException {
ImageReader reader = new IFFImageReader();
// ImageInputStream input = ImageIO.createImageInputStream(new File(pArgs[0]));
ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(new File(pArgs[0])));
boolean canRead = reader.getOriginatingProvider().canDecodeInput(input);
System.out.println("Can read: " + canRead);
if (canRead) {
reader.setInput(input);
ImageReadParam param = reader.getDefaultReadParam();
// param.setSourceRegion(new Rectangle(0, 0, 160, 200));
// param.setSourceRegion(new Rectangle(160, 200, 160, 200));
// param.setSourceRegion(new Rectangle(80, 100, 160, 200));
// param.setDestinationOffset(new Point(80, 100));
// param.setSourceSubsampling(3, 3, 0, 0);
// param.setSourceBands(new int[]{0, 1, 2});
// param.setDestinationBands(new int[]{1, 0, 2});
BufferedImage image = reader.read(0, param);
System.out.println("image = " + image);
showIt(image, pArgs[0]);
}
}
}

View File

@@ -0,0 +1,121 @@
/*
* 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.plugins.iff;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* IFFImageReaderSpi
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFFImageWriterSpi.java,v 1.0 28.feb.2006 19:21:05 haku Exp$
*/
public class IFFImageReaderSpi extends ImageReaderSpi {
static IFFImageReaderSpi mSharedInstance;
/**
* Creates an {@code IFFImageReaderSpi}.
*/
public IFFImageReaderSpi() {
this(IIOUtil.getProviderInfo(IFFImageReaderSpi.class));
}
private IFFImageReaderSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[]{"iff", "IFF"},
new String[]{"iff", "lbm", "ham", "ham8", "ilbm"},
new String[]{"image/iff", "image/x-iff"},
"com.twelvemonkeys.imageio.plugins.iff.IFFImageReader",
STANDARD_INPUT_TYPE,
new String[]{"com.twelvemonkeys.imageio.plugins.iff.IFFImageWriterSpi"},
true, null, null, null, null,
true, null, null, null, null
);
if (mSharedInstance == null) {
mSharedInstance = this;
}
}
public boolean canDecodeInput(Object pSource) throws IOException {
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
}
private static boolean canDecode(ImageInputStream pInput) throws IOException {
pInput.mark();
try {
// Is it IFF
if (pInput.readInt() == IFF.CHUNK_FORM) {
pInput.readInt();// Skip length field
int type = pInput.readInt();
// Is it ILBM or PBM
if (type == IFF.TYPE_ILBM || type == IFF.TYPE_PBM) {
return true;
}
}
}
finally {
pInput.reset();
}
return false;
}
public ImageReader createReaderInstance(Object pExtension) throws IOException {
return new IFFImageReader(this);
}
public String getDescription(Locale pLocale) {
return "Amiga (Electronic Arts) Image Interchange Format (IFF) image reader";
}
public static ImageReaderSpi sharedProvider() {
if (mSharedInstance == null) {
new IFFImageReaderSpi();
}
return mSharedInstance;
}
}

View File

@@ -0,0 +1,271 @@
/*
* 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.plugins.iff;
import com.twelvemonkeys.imageio.ImageWriterBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.enc.PackBitsEncoder;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import java.awt.*;
import java.awt.image.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* Writer for Amiga (Electronic Arts) IFF ILBM (InterLeaved BitMap) format.
* The IFF format (Interchange File Format) is the standard file format
* supported by almost all image software for the Amiga computer.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFFImageWriter.java,v 1.0 02.mar.2006 13:32:30 haku Exp$
*
* @see <a href="http://en.wikipedia.org/wiki/Interchange_File_Format">Wikipedia: IFF</a>
* @see <a href="http://en.wikipedia.org/wiki/ILBM">Wikipedia: IFF ILBM</a>
*/
public class IFFImageWriter extends ImageWriterBase {
public IFFImageWriter() {
this(null);
}
protected IFFImageWriter(ImageWriterSpi pProvider) {
super(pProvider);
}
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
throw new UnsupportedOperationException("Method getDefaultImageMetadata not implemented");// TODO: Implement
}
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
throw new UnsupportedOperationException("Method convertImageMetadata not implemented");// TODO: Implement
}
public void write(IIOMetadata pStreamMetadata, IIOImage pImage, ImageWriteParam pParam) throws IOException {
assertOutput();
if (pImage.hasRaster()) {
throw new UnsupportedOperationException("Cannot write raster");
}
processImageStarted(0);
// Prepare image data to be written
ByteArrayOutputStream imageData = new FastByteArrayOutputStream(1024);
packImageData(imageData, pImage.getRenderedImage(), pParam);
//System.out.println("Image data: " + imageData.size());
// Write metadata
writeMeta(pImage.getRenderedImage(), imageData.size());
// Write image data
writeBody(imageData);
processImageComplete();
}
private void writeBody(ByteArrayOutputStream pImageData) throws IOException {
mImageOutput.writeInt(IFF.CHUNK_BODY);
mImageOutput.writeInt(pImageData.size());
// NOTE: This is much faster than mOutput.write(pImageData.toByteArray())
// as the data array is not duplicated
pImageData.writeTo(IIOUtil.createStreamAdapter(mImageOutput));
if (pImageData.size() % 2 == 0) {
mImageOutput.writeByte(0); // PAD
}
// NOTE: Most progress is done in packImageData, however, as we need to
// buffer, to write correct size, we defer the last 10 percent until now.
processImageProgress(100f);
mImageOutput.flush();
}
private void packImageData(OutputStream pOutput, RenderedImage pImage, ImageWriteParam pParam) throws IOException {
// TODO: Allow param to dictate uncompressed
// TODO: Allow param to dictate type PBM?
// TODO: Subsample/AOI
final boolean compress = shouldCompress(pImage);
final OutputStream output = compress ? new EncoderStream(pOutput, new PackBitsEncoder(), true) : pOutput;
final ColorModel model = pImage.getColorModel();
final Raster raster = pImage.getData();
final int width = pImage.getWidth();
final int height = pImage.getHeight();
// Store each row of pixels
// 0. Loop pr channel
// 1. Convert to planar
// 2. Perform byteRun1 compression for each plane separately
// 3. Write the plane data for each plane
//final int planeWidth = (width + 7) / 8;
final int planeWidth = 2 * ((width + 15) / 16);
final byte[] planeData = new byte[8 * planeWidth];
final int channels = (model.getPixelSize() + 7) / 8;
final int planesPerChannel = channels == 1 ? model.getPixelSize() : 8;
int[] pixels = new int[8 * planeWidth];
// TODO: The spec says "Do not compress across rows!".. I think we currently do.
// NOTE: I'm a little unsure if this is correct for 4 channel (RGBA)
// data, but it is at least consistent with the IFFImageReader for now...
for (int y = 0; y < height; y++) {
for (int c = 0; c < channels; c++) {
pixels = raster.getSamples(0, y, width, 1, c, pixels);
int pixelPos = 0;
int planePos = 0;
for (int i = 0; i < planeWidth; i++) {
IFFUtil.bitRotateCCW(pixels, pixelPos, 1,
planeData, planePos, planeWidth);
pixelPos += 8;
planePos++;
}
for (int p = 0; p < planesPerChannel; p++) {
output.write(planeData, p * planeWidth, planeWidth);
if (!compress && planeWidth % 2 != 0) {
output.write(0); // PAD
}
}
}
processImageProgress(y * 90f / height);
}
output.flush();
}
private void writeMeta(RenderedImage pImage, int pBodyLength) throws IOException {
// Annotation ANNO chunk, 8 + annoData.length bytes
String annotation = "Written by " + getOriginatingProvider().getDescription(null);
GenericChunk anno = new GenericChunk(IFFUtil.toInt("ANNO".getBytes()), annotation.getBytes());
ColorModel cm = pImage.getColorModel();
IndexColorModel icm = null;
// Bitmap header BMHD chunk, 8 + 20 bytes
// By default, don't compress narrow images
int compression = shouldCompress(pImage) ? BMHDChunk.COMPRESSION_BYTE_RUN : BMHDChunk.COMPRESSION_NONE;
BMHDChunk header;
if (cm instanceof IndexColorModel) {
//System.out.println("IndexColorModel");
icm = (IndexColorModel) cm;
int trans = icm.getTransparency() == Transparency.BITMASK ? BMHDChunk.MASK_TRANSPARENT_COLOR : BMHDChunk.MASK_NONE;
int transPixel = icm.getTransparency() == Transparency.BITMASK ? icm.getTransparentPixel() : 0;
header = new BMHDChunk(pImage.getWidth(), pImage.getHeight(), icm.getPixelSize(),
trans, compression, transPixel);
}
else {
//System.out.println(cm.getClass().getName());
header = new BMHDChunk(pImage.getWidth(), pImage.getHeight(), cm.getPixelSize(),
BMHDChunk.MASK_NONE, compression, 0);
}
// Colormap CMAP chunk, 8 + icm.getMapSize() * 3 bytes (+ 1 optional pad).
CMAPChunk cmap = null;
if (icm != null) {
//System.out.println("CMAP!");
cmap = new CMAPChunk(icm);
}
// ILBM(4) + anno(8+len) + header(8+20) + cmap(8+len)? + body(8+len);
int size = 4 + 8 + anno.mChunkLength + 28 + 8 + pBodyLength;
if (cmap != null) {
size += 8 + cmap.mChunkLength;
}
mImageOutput.writeInt(IFF.CHUNK_FORM);
mImageOutput.writeInt(size);
mImageOutput.writeInt(IFF.TYPE_ILBM);
anno.writeChunk(mImageOutput);
header.writeChunk(mImageOutput);
if (cmap != null) {
//System.out.println("CMAP written");
cmap.writeChunk(mImageOutput);
}
}
private boolean shouldCompress(RenderedImage pImage) {
return pImage.getWidth() >= 32;
}
public static void main(String[] pArgs) throws IOException {
BufferedImage image = ImageIO.read(new File(pArgs[0]));
ImageWriter writer = new IFFImageWriter(new IFFImageWriterSpi());
writer.setOutput(ImageIO.createImageOutputStream(new File(pArgs[1])));
//writer.addIIOWriteProgressListener(new ProgressListenerBase() {
// int mCurrPct = 0;
//
// public void imageComplete(ImageWriter pSource) {
// mCurrPct = 100;
// printProgress(mCurrPct);
// }
//
// public void imageProgress(ImageWriter pSource, float pPercentageDone) {
// if ((int) pPercentageDone > mCurrPct) {
// printProgress((int) pPercentageDone);
// mCurrPct = (int) pPercentageDone;
// }
// }
//
// private void printProgress(int pCurrPct) {
// if (mCurrPct == 0) {
// System.out.print("[");
// }
// for (int i = mCurrPct / 2; i < pCurrPct / 2; i++) {
// System.out.print(".");
// }
// if (mCurrPct == 100) {
// System.out.println("]");
// }
// }
//});
//image = com.twelvemonkeys.image.ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB);
writer.write(image);
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.plugins.iff;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
import java.io.IOException;
import java.util.Locale;
/**
* IFFImageWriterSpi
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFFImageWriterSpi.java,v 1.0 02.mar.2006 19:21:05 haku Exp$
*/
public class IFFImageWriterSpi extends ImageWriterSpi {
/**
* Creates an {@code IFFImageWriterSpi}.
*/
public IFFImageWriterSpi() {
this(IIOUtil.getProviderInfo(IFFImageWriterSpi.class));
}
private IFFImageWriterSpi(final ProviderInfo pProviderInfo) {
super(
pProviderInfo.getVendorName(),
pProviderInfo.getVersion(),
new String[]{"iff", "IFF"},
new String[]{"iff", "lbm", "ham", "ham8", "ilbm"},
new String[]{"image/iff", "image/x-iff"},
"com.twelvemonkeys.imageio.plugins.iff.IFFImageWriter",
STANDARD_OUTPUT_TYPE,
new String[]{"com.twelvemonkeys.imageio.plugins.iff.IFFImageReaderSpi"},
true, null, null, null, null,
true, null, null, null, null
);
}
public boolean canEncodeImage(final ImageTypeSpecifier pType) {
// TODO: Probably can't store 16 bit types etc...
// TODO: Can't store CMYK (well.. it does, but they can't be read back)
return true;
}
public ImageWriter createWriterInstance(Object pExtension) throws IOException {
return new IFFImageWriter(this);
}
public String getDescription(Locale pLocale) {
return "Amiga (Electronic Arts) IFF image writer";
}
}

View File

@@ -0,0 +1,256 @@
/*
* 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.
*/
/*
* Fast 90-degree bit rotation routines.
*
* Based on Sue-Ken Yap, "A Fast 90-Degree Bitmap Rotator," in GRAPHICS
* GEMS II, James Arvo ed., Academic Press, 1991, ISBN 0-12-064480-0.
*/
package com.twelvemonkeys.imageio.plugins.iff;
/**
* IFFUtil
* <p/>
* Bit rotate methods based on Sue-Ken Yap, "A Fast 90-Degree Bitmap Rotator,"
* in GRAPHICS GEMS II, James Arvo ed., Academic Press, 1991, ISBN 0-12-064480-0.
*
* @author Unascribed (C version)
* @author Harald Kuhr (Java port)
* @version $Id: IFFUtil.java,v 1.0 06.mar.2006 13:31:35 haku Exp$
*/
class IFFUtil {
/**
* Creates a rotation table
* @param n number of bits -1
*
* @return the rotation table
*/
static private long[] rtable(int n) {
return new long[]{
0x00000000l << n, 0x00000001l << n, 0x00000100l << n, 0x00000101l << n,
0x00010000l << n, 0x00010001l << n, 0x00010100l << n, 0x00010101l << n,
0x01000000l << n, 0x01000001l << n, 0x01000100l << n, 0x01000101l << n,
0x01010000l << n, 0x01010001l << n, 0x01010100l << n, 0x01010101l << n
};
}
static private final long[][] RTABLE = {
rtable(0), rtable(1), rtable(2), rtable(3),
rtable(4), rtable(5), rtable(6), rtable(7)
};
/**
* Rotate bits clockwise.
* The IFFImageReader uses this to convert pixel bits from planar to chunky.
* Bits from the source are rotated 90 degrees clockwise written to the
* destination.
*
* @param pSrc source pixel data
* @param pSrcPos starting index of 8 x 8 bit source tile
* @param pSrcStep byte offset between adjacent rows in source
* @param pDst destination pixel data
* @param pDstPos starting index of 8 x 8 bit destination tile
* @param pDstStep byte offset between adjacent rows in destination
*/
static void bitRotateCW(final byte[] pSrc, int pSrcPos, int pSrcStep,
final byte[] pDst, int pDstPos, int pDstStep) {
int idx = pSrcPos;
int lonyb;
int hinyb;
long lo = 0;
long hi = 0;
for (int i = 0; i < 8; i++) {
lonyb = pSrc[idx] & 0xF;
hinyb = (pSrc[idx] >> 4) & 0xF;
lo |= RTABLE[i][lonyb];
hi |= RTABLE[i][hinyb];
idx += pSrcStep;
}
idx = pDstPos;
pDst[idx] = (byte)((hi >> 24) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)((hi >> 16) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)((hi >> 8) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)(hi & 0xFF);
idx += pDstStep;
}
}
}
if (idx < pDst.length) {
pDst[idx] = (byte)((lo >> 24) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)((lo >> 16) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)((lo >> 8) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)(lo & 0xFF);
}
}
}
}
}
/**
* Rotate bits counterclockwise.
* The IFFImageWriter uses this to convert pixel bits from chunky to planar.
*
* @param pSrc source pixel data (only lower 8 bits used)
* @param pSrcPos starting index of 8 x 8 bit source tile
* @param pSrcStep byte offset between adjacent rows in source
* @param pDst destination pixel data
* @param pDstPos starting index of 8 x 8 bit destination tile
* @param pDstStep byte offset between adjacent rows in destination
*/
static void bitRotateCCW(final int[] pSrc, int pSrcPos, int pSrcStep,
final byte[] pDst, int pDstPos, int pDstStep) {
int idx = pSrcPos;
int lonyb;
int hinyb;
long lo = 0;
long hi = 0;
for (int i = 7; i >= 0; i--) {
lonyb = pSrc[idx] & 0xF;
hinyb = (pSrc[idx] >> 4) & 0xF;
lo |= RTABLE[i][lonyb];
hi |= RTABLE[i][hinyb];
idx += pSrcStep;
}
idx = pDstPos;
pDst[idx] = (byte)(lo & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 8) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 16) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 24) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)(hi & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 8) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 16) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 24) & 0xFF);
}
/**
* Rotate bits counterclockwise.
* The IFFImageWriter uses this to convert pixel bits from chunky to planar.
*
* @param pSrc source pixel data
* @param pSrcPos starting index of 8 x 8 bit source tile
* @param pSrcStep byte offset between adjacent rows in source
* @param pDst destination pixel data
* @param pDstPos starting index of 8 x 8 bit destination tile
* @param pDstStep byte offset between adjacent rows in destination
*/
static void bitRotateCCW(final byte[] pSrc, int pSrcPos, int pSrcStep,
final byte[] pDst, int pDstPos, int pDstStep) {
int idx = pSrcPos;
int lonyb;
int hinyb;
long lo = 0;
long hi = 0;
for (int i = 7; i >= 0; i--) {
lonyb = pSrc[idx] & 0xF;
hinyb = (pSrc[idx] >> 4) & 0xF;
lo |= RTABLE[i][lonyb];
hi |= IFFUtil.RTABLE[i][hinyb];
idx += pSrcStep;
}
idx = pDstPos;
pDst[idx] = (byte)(lo & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 8) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 16) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 24) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)(hi & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 8) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 16) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 24) & 0xFF);
}
/**
* Converts a byte array to an int.
*
* @param pBytes a byte array of length 4
* @return the bytes converted to an int
*
* @throws ArrayIndexOutOfBoundsException if length is < 4
*/
static int toInt(final byte[] pBytes) {
return (pBytes[0] & 0xff) << 24 | (pBytes[1] & 0xff) << 16
| (pBytes[2] & 0xff) << 8 | (pBytes[3] & 0xff);
}
/**
* Converts an int to a four letter String.
*
* @param pChunkId the chunk identifier
* @return a String
*/
static String toChunkStr(int pChunkId) {
return new String(new byte[] {(byte) ((pChunkId & 0xff000000) >> 24),
(byte) ((pChunkId & 0x00ff0000) >> 16),
(byte) ((pChunkId & 0x0000ff00) >> 8),
(byte) ((pChunkId & 0x000000ff))});
}
}

View File

@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.iff.IFFImageReaderSpi

View File

@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.iff.IFFImageWriterSpi

View File

@@ -0,0 +1,85 @@
/*
* 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.plugins.iff;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/**
* IFFImageReaderTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IFFImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class IFFImageReaderTestCase extends ImageReaderAbstractTestCase<IFFImageReader> {
// TODO: Need test for IFF PBM
protected List<TestData> getTestData() {
return Arrays.asList(
// 32 bit - Ok
new TestData(getClassLoaderResource("/iff/test.iff"), new Dimension(300, 200)), // 32 bit
// 24 bit - Ok
new TestData(getClassLoaderResource("/iff/survivor.iff"), new Dimension(800, 600)), // 24 bit
// HAM6 - Ok (a lot of visual "fringe", would be interesting to see on a real HAM display)
new TestData(getClassLoaderResource("/iff/A4000T_HAM6.IFF"), new Dimension(320, 512)), // ham6
// HAM8 - Ok (PackBits decoder chokes on padding byte)
new TestData(getClassLoaderResource("/iff/A4000T_HAM8.IFF"), new Dimension(628, 512)), // ham8
// 8 color indexed - Ok
new TestData(getClassLoaderResource("/iff/AmigaBig.iff"), new Dimension(300, 200)), // 8 color
// 8 color indexed - Ok
new TestData(getClassLoaderResource("/iff/AmigaAmiga.iff"), new Dimension(200, 150)), // 8 color
// Ok (PackBits decoder chokes on padding byte)
new TestData(getClassLoaderResource("/iff/Abyss.iff"), new Dimension(320, 400))
);
}
protected ImageReaderSpi createProvider() {
return new IFFImageReaderSpi();
}
protected Class<IFFImageReader> getReaderClass() {
return IFFImageReader.class;
}
protected List<String> getFormatNames() {
return Arrays.asList("iff");
}
protected List<String> getSuffixes() {
return Arrays.asList("iff", "ilbm", "ham", "ham8", "lbm");
}
protected List<String> getMIMETypes() {
return Arrays.asList("image/iff", "image/x-iff");
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

4
imageio/imageio-iff/todo.txt Executable file
View File

@@ -0,0 +1,4 @@
- Fix the crashes with "java.io.EOFException: Unexpected end of PackBits stream"
- Fix the bugs with trashed images
- Have a look at Werner Randelshofer's ANIM applet, maybe we can "borrow" the the ANIM support. :-)
DONE:

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,41 @@
<?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-jmagick</artifactId>
<version>2.2</version>
<name>TwelveMonkeys ImageIO JMagick Plugin</name>
<description>
<![CDATA[
ImageIO wrapper for JMagick.
See the <a href="http://www.yeo.id.au/jmagick/">JMagick Home page</a>
for more information.]]>
</description>
<parent>
<artifactId>twelvemonkeys-imageio</artifactId>
<groupId>com.twelvemonkeys</groupId>
<version>2.2</version>
</parent>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>jmagick</groupId>
<artifactId>jmagick</artifactId>
<version>6.2.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,42 @@
/*
* 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.plugins.jmagick;
/**
* BMPImageReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BMPImageReader.java,v 1.0 Dec 2, 2007 9:03:37 PM haraldk Exp$
*/
public final class BMPImageReader extends JMagickReader {
public BMPImageReader(final BMPImageReaderSpi pProvider) {
super(pProvider);
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.plugins.jmagick;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* BMPImageReaderSpi
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: BMPImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $
*/
public class BMPImageReaderSpi extends JMagickImageReaderSpiSupport {
public BMPImageReaderSpi() {
super(
new String[]{"bmp", "BMP"},
new String[]{"bmp", "rle", "dib"},
new String[]{"image/bmp", "image/x-bmp", "image/x-windows-bmp", "image/x-ms-bmp"}, // http://en.wikipedia.org/wiki/Windows_Bitmap
BMPImageReader.class.getName(),
new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.BMPImageWriterSpi"}
);
}
@Override
public BMPImageReader createReaderImpl(Object pExtension) throws IOException {
return new BMPImageReader(this);
}
@Override
public boolean canDecode(ImageInputStream pSource) throws IOException {
//new byte[][] {new byte[] {'B', 'M'}, new byte[] {'M', 'B'}, }, // BMP
//new byte[][] {new byte[] {0x00, 0x00, 0x02, 0x00,
// 0x01, 0x00, 0x20, 0x20}}, // CUR
//new byte[][] {new byte[] {0x0, 0x0, 0x1, 0x0}}, // ICO (best guess)
byte[] magic = new byte[2];
pSource.readFully(magic);
return (magic[0] == 'B' && magic[1] == 'M') || (magic[0] == 'M' && magic[1] == 'B');
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.plugins.jmagick;
import javax.imageio.ImageWriteParam;
/**
* BMPImageWriter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BMPImageWriter.java,v 1.0 Dec 3, 2007 12:04:10 PM haraldk Exp$
*/
public final class BMPImageWriter extends JMagickWriter {
protected BMPImageWriter(final BMPImageWriterSpi pProvider) {
super(pProvider);
}
protected ImageWriteParam createDefaultWriteParam() {
return new ImageWriteParam(getLocale()) {
@Override
public boolean canWriteCompressed() {
return true;
}
@Override
public boolean isCompressionLossless() {
return true;
}
};
}
}

Some files were not shown because too many files have changed in this diff Show More