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