diff --git a/twelvemonkeys-imageio/batik/license.txt b/twelvemonkeys-imageio/batik/license.txt new file mode 100755 index 00000000..2d8ee79c --- /dev/null +++ b/twelvemonkeys-imageio/batik/license.txt @@ -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. \ No newline at end of file diff --git a/twelvemonkeys-imageio/batik/pom.xml b/twelvemonkeys-imageio/batik/pom.xml new file mode 100755 index 00000000..c76f6aa7 --- /dev/null +++ b/twelvemonkeys-imageio/batik/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + com.twelvemonkeys.imageio + twelvemonkeys-imageio-batik + 2.1 + TwelveMonkeys ImageIO Batik Plugin + + Batik Home page + for more information.]]> + + + + twelvemonkeys-imageio + com.twelvemonkeys + 2.1 + + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + tests + + + + batik + batik-rasterizer-ext + 1.6-1 + provided + + + + batik + batik-svggen + 1.6-1 + provided + + + + batik + batik-transcoder + 1.6-1 + provided + + + + + fop + fop + + + + + \ No newline at end of file diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReader.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReader.java new file mode 100755 index 00000000..76305468 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReader.java @@ -0,0 +1,553 @@ +/* + * 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.Iterator; +import java.util.Map; + +/** + * Image reader for SVG document fragments. + *

+ * + * @author Harald Kuhr + * @author Inpspired by code from the Batik Team + * @version $Id: $ + * @see batik-dev + */ +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(); + } + + // Read in the image, using the Batik Transcoder + try { + processImageStarted(pIndex); + + mRasterizer.mTranscoderInput.setURI(baseURI); + BufferedImage image = mRasterizer.getImage(); + + processImageComplete(); + + return image; + } + 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 getImageTypes(int imageIndex) throws IOException { + throw new UnsupportedOperationException("Method getImageTypes not implemented");// TODO: Implement + } + + /** + * An image transcoder that stores the resulting image. + *

+ * 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; + } + } +} diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReaderSpi.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReaderSpi.java new file mode 100755 index 00000000..3b536f05 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReaderSpi.java @@ -0,0 +1,159 @@ +/* + * 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.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 + *

+ * + * @author Harald Kuhr + * @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 SVGImageReaderSpi + */ + public SVGImageReaderSpi() { + super( + "TwelveMonkeys", // Vendor name + "2.0", // 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"; + } + + @Override + public void onRegistration(ServiceRegistry registry, Class category) { + if (!SVG_READER_AVAILABLE) { + IIOUtil.deregisterProvider(registry, this, category); + } + }} + diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGReadParam.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGReadParam.java new file mode 100755 index 00000000..a4ec26f4 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/svg/SVGReadParam.java @@ -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; + } +} diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java new file mode 100755 index 00000000..caab3b4c --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReader.java @@ -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 Harald Kuhr + * @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 mImages = new ArrayList(); + + 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 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; + } +} diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderSpi.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderSpi.java new file mode 100755 index 00000000..d0510d00 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderSpi.java @@ -0,0 +1,119 @@ +/* + * 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.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 + *

+ * + * @author Harald Kuhr + * @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 an SVGImageReaderSpi + */ + public TIFFImageReaderSpi() { + super( + "TwelveMonkeys", // Vendor name + "2.0", // 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"; + } + + @Override + public void onRegistration(ServiceRegistry registry, Class category) { + if (!TIFF_CLASSES_AVAILABLE) { + IIOUtil.deregisterProvider(registry, this, category); + } + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java new file mode 100755 index 00000000..212dc2f4 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriter.java @@ -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 Harald Kuhr + * @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); + } + } +} diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterSpi.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterSpi.java new file mode 100755 index 00000000..8ec02f3b --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterSpi.java @@ -0,0 +1,100 @@ +/* + * 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.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 Harald Kuhr + * @version $Id: TIFFImageWriterSpi.java,v 1.2 2004/01/14 15:21:44 wmhakur Exp $ + */ +public class TIFFImageWriterSpi extends ImageWriterSpi { + + /** + * Creates a TIFFImageWriterSpi. + */ + public TIFFImageWriterSpi() { + super( + "TwelveMonkeys", // Vendor name + "2.0", // 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"; + } + + public void onRegistration(ServiceRegistry registry, Class category) { + if (!TIFFImageReaderSpi.TIFF_CLASSES_AVAILABLE) { + IIOUtil.deregisterProvider(registry, this, category); + } + } +} diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMF.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMF.java new file mode 100755 index 00000000..08159719 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMF.java @@ -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 Harald Kuhr + * @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, + }; +} diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReader.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReader.java new file mode 100755 index 00000000..d650e107 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReader.java @@ -0,0 +1,136 @@ +/* + * 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 Harald Kuhr + * @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 getImageTypes(final int imageIndex) throws IOException { + throw new UnsupportedOperationException("Method getImageTypes not implemented");// TODO: Implement + } + +} diff --git a/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReaderSpi.java b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReaderSpi.java new file mode 100755 index 00000000..c59d75e0 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReaderSpi.java @@ -0,0 +1,122 @@ +/* + * 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.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 + *

+ * + * @author Harald Kuhr + * @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 an SVGImageReaderSpi + */ + public WMFImageReaderSpi() { + super( + "TwelveMonkeys", // Vendor name + "2.0", // 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"; + } + + @Override + public void onRegistration(ServiceRegistry registry, Class category) { + if (!WMF_READER_AVAILABLE) { + IIOUtil.deregisterProvider(registry, this, category); + } + } +} + diff --git a/twelvemonkeys-imageio/batik/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/twelvemonkeys-imageio/batik/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi new file mode 100755 index 00000000..42f4f345 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi @@ -0,0 +1,3 @@ +com.twelvemonkeys.imageio.plugins.svg.SVGImageReaderSpi +com.twelvemonkeys.imageio.plugins.wmf.WMFImageReaderSpi +#com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi diff --git a/twelvemonkeys-imageio/batik/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi b/twelvemonkeys-imageio/batik/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi new file mode 100755 index 00000000..54dbaa61 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi @@ -0,0 +1 @@ +#com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriterSpi \ No newline at end of file diff --git a/twelvemonkeys-imageio/batik/src/test/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReaderTestCase.java b/twelvemonkeys-imageio/batik/src/test/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReaderTestCase.java new file mode 100755 index 00000000..6821bc33 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/test/java/com/twelvemonkeys/imageio/plugins/svg/SVGImageReaderTestCase.java @@ -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 Harald Kuhr + * @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 { + private SVGImageReaderSpi mSVGImageReaderSpi = new SVGImageReaderSpi(); + + protected List 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 getReaderClass() { + return SVGImageReader.class; + } + + protected List getFormatNames() { + return Arrays.asList("svg"); + } + + protected List getSuffixes() { + return Arrays.asList("svg"); + } + + protected List getMIMETypes() { + return Arrays.asList("image/svg+xml"); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/batik/src/test/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReaderTestCase.java b/twelvemonkeys-imageio/batik/src/test/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReaderTestCase.java new file mode 100755 index 00000000..e07d8f2e --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/test/java/com/twelvemonkeys/imageio/plugins/wmf/WMFImageReaderTestCase.java @@ -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 Harald Kuhr + * @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 { + private WMFImageReaderSpi mSVGImageReaderSpi = new WMFImageReaderSpi(); + + protected List 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 getReaderClass() { + return WMFImageReader.class; + } + + protected List getFormatNames() { + return Arrays.asList("wmf"); + } + + protected List getSuffixes() { + return Arrays.asList("wmf", "emf"); + } + + protected List getMIMETypes() { + return Arrays.asList("image/x-wmf", "application/x-msmetafile"); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/batik/src/test/resources/svg/batikLogo.svg b/twelvemonkeys-imageio/batik/src/test/resources/svg/batikLogo.svg new file mode 100755 index 00000000..ba954486 --- /dev/null +++ b/twelvemonkeys-imageio/batik/src/test/resources/svg/batikLogo.svg @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + Batik Logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/twelvemonkeys-imageio/batik/src/test/resources/wmf/test.wmf b/twelvemonkeys-imageio/batik/src/test/resources/wmf/test.wmf new file mode 100755 index 00000000..247d3698 Binary files /dev/null and b/twelvemonkeys-imageio/batik/src/test/resources/wmf/test.wmf differ diff --git a/twelvemonkeys-imageio/core/license.txt b/twelvemonkeys-imageio/core/license.txt new file mode 100755 index 00000000..2d8ee79c --- /dev/null +++ b/twelvemonkeys-imageio/core/license.txt @@ -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. \ No newline at end of file diff --git a/twelvemonkeys-imageio/core/pom.xml b/twelvemonkeys-imageio/core/pom.xml new file mode 100755 index 00000000..f702c705 --- /dev/null +++ b/twelvemonkeys-imageio/core/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + 2.1 + TwelveMonkeys ImageIO Core + + + twelvemonkeys-imageio + com.twelvemonkeys + 2.1 + + \ No newline at end of file diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageReaderBase.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageReaderBase.java new file mode 100755 index 00000000..5f36ff01 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageReaderBase.java @@ -0,0 +1,381 @@ +/* + * 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.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +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.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; + +/** + * ImageReaderBase + * + * @author Harald Kuhr + * @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. + *

+ *

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 pOriginatingProvider the {@code ImageReaderSpi} that is + * invoking this constructor, or {@code null}. + */ + protected ImageReaderBase(final ImageReaderSpi pOriginatingProvider) { + super(pOriginatingProvider); + } + + /** + * 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(Object pInput, boolean pSeekForwardOnly, 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: + *

+ * + */ + protected abstract void resetMembers(); + + /** + * Defaul implementation that always return {@code null}. + * + * @param pImageIndex ignored, unless overriden + * @return {@code null}, unless overriden + * @throws IOException never, unless overriden. + */ + public IIOMetadata getImageMetadata(int pImageIndex) throws IOException { + return null; + } + + /** + * Defaul implementation that always return {@code null}. + * + * @return {@code null}, unless overriden + * @throws IOException never, unless overriden. + */ + public IIOMetadata getStreamMetadata() throws IOException { + return null; + } + + /** + * Default implementation that always returns {@code 1}. + * + * @param pAllowSearch ignored, unless overriden + * @return {@code 1}, unless overriden + * @throws IOException never, unless overriden + */ + 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 + * 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"); + } + } + + /** + * 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. + *

+ * 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. + *

+ * NOTE: This method does not take the subsampling offsets into + * consideration. + *

+ * 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])); + 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; + + public ImageLabel(BufferedImage pImage) { + super(new BufferedImageIcon(pImage)); + setOpaque(false); + setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); + + mBackground = createTexture(); + + 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("Default", mBackground), 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...", 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 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); + } + } + } + } +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageWriterBase.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageWriterBase.java new file mode 100755 index 00000000..12fab584 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/ImageWriterBase.java @@ -0,0 +1,158 @@ +/* + * 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.metadata.IIOMetadata; +import javax.imageio.spi.ImageWriterSpi; +import javax.imageio.stream.ImageOutputStream; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * ImageWriterBase + * + * @author Harald Kuhr + * @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 javax.imageio.ImageWriter { + protected ImageOutputStream mImageOutput; + + /** + * Constructs an {@code ImageWriter} and sets its + * {@code originatingProvider} instance variable to the + * supplied value. + *

+ *

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(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 igonred. + * @return {@code null}. + */ + public IIOMetadata getDefaultStreamMetadata(javax.imageio.ImageWriteParam pParam) { + return null; + } + + /** + * Returns {@code null} + * + * @param pInData ignored. + * @param pParam igonred. + * @return {@code null}. + */ + public IIOMetadata convertStreamMetadata(IIOMetadata pInData, ImageWriteParam pParam) { + return null; + } + + protected static Rectangle getSourceRegion(ImageWriteParam pParam, int pWidth, 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 IIOParam#setSourceRegion(java.awt.Rectangle)} + * method. + *

+ * 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, 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 IIOParam#setSourceSubsampling(int, int, int, int)} + * method. + *

+ * NOTE: This method does not take the subsampling offsets into + * consideration. + *

+ * 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, ImageWriteParam pParam) { + return IIOUtil.fakeSubsampling(pImage, pParam); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStream.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStream.java new file mode 100755 index 00000000..fa5cb7a4 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStream.java @@ -0,0 +1,171 @@ +package com.twelvemonkeys.imageio.stream; + +import com.twelvemonkeys.lang.Validate; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageInputStreamImpl; +import java.io.IOException; + +/** + * BufferedFileImageInputStream + * Experimental - seems to be effective for FileImageInputStream and FileCacheImageInputStream. + * + * @author Harald Kuhr + * @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? +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) { + this(pStream, DEFAULT_BUFFER_SIZE); + } + + private BufferedImageInputStream(final ImageInputStream pStream, final int pBufferSize) { + Validate.notNull(pStream, "stream"); + + mStream = pStream; + 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; + + boolean bypassBuffer = false; + + if (!isBufferValid()) { + // Bypass cache if cache is empty for reads longer than buffer + if (pLength >= mBuffer.length) { + bypassBuffer = true; + } + else { + fillBuffer(); + } + } + + if (!bypassBuffer && mBufferLength <= 0) { + return -1; + } + + // Read as much as possible from buffer + int length = bypassBuffer ? 0 : Math.min(mBufferLength - mBufferPos, pLength); + + if (length > 0) { + System.arraycopy(mBuffer, mBufferPos, pBuffer, pOffset, length); + mBufferPos += length; + } + + // Read rest directly from stream, if longer than buffer + if (pLength - length >= mBuffer.length) { + int read = mStream.read(pBuffer, pOffset + length, pLength - length); + + if (read > 0) { + length += read; + } + } + + streamPos += length; + + return length; + } + + @Override + public void seek(long pPosition) throws IOException { + // TODO: Could probably be optimized to not invalidate buffer if new pos 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 unchecked(IOException pExcption, Class pClass) { + // Ugly hack to fool the compiler.. + return (T) pExcption; + } +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStream.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStream.java new file mode 100755 index 00000000..1c2119df --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStream.java @@ -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 Harald Kuhr + * @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; + } +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamSpi.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamSpi.java new file mode 100755 index 00000000..ae148269 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamSpi.java @@ -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 Harald Kuhr + * @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"; + } + +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/URLImageInputStreamSpi.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/URLImageInputStreamSpi.java new file mode 100755 index 00000000..7518bc0a --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/stream/URLImageInputStreamSpi.java @@ -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 Harald Kuhr + * @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()))); +// return new FileImageInputStream(new File(url.toURI())); + } + catch (URISyntaxException ignore) { + 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"; + } +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOInputStreamAdapter.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOInputStreamAdapter.java new file mode 100755 index 00000000..1941efff --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOInputStreamAdapter.java @@ -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 + *

+ * Note: You should always wrap this stream in a {@code BufferedInputStream}. + * If not, performance may degrade significantly. +* +* @author Harald Kuhr +* @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 not 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; + } +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOOutputStreamAdapter.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOOutputStreamAdapter.java new file mode 100755 index 00000000..9927b983 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOOutputStreamAdapter.java @@ -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 Harald Kuhr + * @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; + } +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java new file mode 100755 index 00000000..f6698c37 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IIOUtil.java @@ -0,0 +1,131 @@ +package com.twelvemonkeys.imageio.util; + +import com.twelvemonkeys.image.ImageUtil; + +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 Harald Kuhr + * @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)); + } + + /* + * THIS METHOD WILL ME MOVED/RENAMED, DO NOT USE. + */ + public static void deregisterProvider(final ServiceRegistry pRegistry, final IIOServiceProvider pProvider, final Class 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); + } + + 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; + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifier.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifier.java new file mode 100755 index 00000000..42cf52d0 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifier.java @@ -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 Harald Kuhr + * @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!"); + } + } + +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/ProgressListenerBase.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/ProgressListenerBase.java new file mode 100755 index 00000000..4a4ba7ff --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/ProgressListenerBase.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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) { + } +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/ReaderFileSuffixFilter.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/ReaderFileSuffixFilter.java new file mode 100755 index 00000000..bdad8e28 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/ReaderFileSuffixFilter.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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 mKnownSuffixes = new HashMap(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; + } +} diff --git a/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/WriterFileSuffixFilter.java b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/WriterFileSuffixFilter.java new file mode 100755 index 00000000..935faebd --- /dev/null +++ b/twelvemonkeys-imageio/core/src/main/java/com/twelvemonkeys/imageio/util/WriterFileSuffixFilter.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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 MapmKnownSuffixes = new HashMap(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; + } +} diff --git a/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStreamTestCase.java b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStreamTestCase.java new file mode 100755 index 00000000..b7b33390 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStreamTestCase.java @@ -0,0 +1,116 @@ +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 Harald Kuhr + * @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() { + new BufferedImageInputStream(new ByteArrayImageInputStream(new byte[0])); + } + + public void testCreateNull() { + 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)); + } + + /** + * 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; + } +} diff --git a/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamTestCase.java b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamTestCase.java new file mode 100755 index 00000000..372fe6ed --- /dev/null +++ b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamTestCase.java @@ -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 Harald Kuhr + * @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)); + } + } +} diff --git a/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/IIOInputStreamAdapterTestCase.java b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/IIOInputStreamAdapterTestCase.java new file mode 100755 index 00000000..b010109f --- /dev/null +++ b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/IIOInputStreamAdapterTestCase.java @@ -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 Harald Kuhr + * @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()); + + } +} diff --git a/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageReaderAbstractTestCase.java b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageReaderAbstractTestCase.java new file mode 100755 index 00000000..7a446284 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageReaderAbstractTestCase.java @@ -0,0 +1,1373 @@ +/* + * 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.imageio.stream.URLImageInputStreamSpi; +import org.jmock.Mock; +import org.jmock.cglib.MockObjectTestCase; +import org.jmock.core.Invocation; +import org.jmock.core.Stub; + +import javax.imageio.*; +import javax.imageio.event.IIOReadProgressListener; +import javax.imageio.spi.IIORegistry; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * ImageReaderAbstractTestCase + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ImageReaderAbstractTestCase.java,v 1.0 Apr 1, 2008 10:36:46 PM haraldk Exp$ + */ +public abstract class ImageReaderAbstractTestCase extends MockObjectTestCase { + // TODO: Should we really test if he provider is installed? + // - Pro: Tests the META-INF/services config + // - Con: Not all providers should be installed at runtime... + + static { + IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi()); + } + + protected abstract List getTestData(); + + /** + * Convenience method to get a list of test files from the classpath. + * Currently only works for resources on the filesystem (not in jars or + * archives). + * + * @param pResourceInFolder a resource in the correct classpath folder. + * @return a list of files + */ + protected final List getInputsFromClasspath(final String pResourceInFolder) { + URL resource = getClass().getClassLoader().getResource(pResourceInFolder); + assertNotNull(resource); + File dir; + try { + dir = new File(resource.toURI()).getParentFile(); + } + catch (URISyntaxException e) { + throw new RuntimeException(e); + } + List files = Arrays.asList(dir.listFiles()); + assertFalse(files.isEmpty()); + return files; + } + + protected abstract ImageReaderSpi createProvider(); + + protected abstract Class getReaderClass(); + + protected T createReader() { + try { + return getReaderClass().newInstance(); + } + catch (InstantiationException e) { + throw new RuntimeException(e); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + protected abstract List getFormatNames(); + + protected abstract List getSuffixes(); + + protected abstract List getMIMETypes(); + + protected void assertProviderInstalledForName(final String pFormat, final Class pReaderClass) { + assertProviderInstalled0(pFormat.toUpperCase(), pReaderClass, ImageIO.getImageReadersByFormatName(pFormat.toUpperCase())); + assertProviderInstalled0(pFormat.toLowerCase(), pReaderClass, ImageIO.getImageReadersByFormatName(pFormat.toLowerCase())); + } + + protected void assertProviderInstalledForMIMEType(final String pType, final Class pReaderClass) { + assertProviderInstalled0(pType, pReaderClass, ImageIO.getImageReadersByMIMEType(pType)); + } + + protected void assertProviderInstalledForSuffix(final String pType, final Class pReaderClass) { + assertProviderInstalled0(pType, pReaderClass, ImageIO.getImageReadersBySuffix(pType)); + } + + private void assertProviderInstalled0(final String pFormat, final Class pReaderClass, final Iterator pReaders) { + boolean found = false; + while (pReaders.hasNext()) { + ImageReader reader = pReaders.next(); + if (reader.getClass() == pReaderClass) { + found = true; + } + } + + assertTrue(pReaderClass.getSimpleName() + " not installed for " + pFormat, found); + } + + public void testProviderInstalledForNames() { + Class readerClass = getReaderClass(); + for (String name : getFormatNames()) { + assertProviderInstalledForName(name, readerClass); + } + } + + public void testProviderInstalledForSuffixes() { + Class readerClass = getReaderClass(); + for (String suffix : getSuffixes()) { + assertProviderInstalledForSuffix(suffix, readerClass); + } + } + + public void testProviderInstalledForMIMETypes() { + Class readerClass = getReaderClass(); + for (String type : getMIMETypes()) { + assertProviderInstalledForMIMEType(type, readerClass); + } + } + + public void testProviderCanRead() throws IOException { + List testData = getTestData(); + + ImageReaderSpi provider = createProvider(); + for (TestData data : testData) { + ImageInputStream stream = data.getInputStream(); + assertNotNull(stream); + assertTrue("Provider is expected to be able to decode data: " + data, provider.canDecodeInput(stream)); + } + } + + public void testProviderCanReadNull() { + boolean canRead = false; + try { + canRead = createProvider().canDecodeInput(null); + } + catch (IllegalArgumentException ignore) { + } + catch (RuntimeException e) { + fail("RuntimeException other than IllegalArgumentException thrown: " + e); + } + catch (IOException e) { + fail("Could not test data for read: " + e); + } + assertFalse("ImageReader can read null input", canRead); + } + + public void testSetInput() { + // Should just pass with no exceptions + ImageReader reader = createReader(); + assertNotNull(reader); + for (TestData data : getTestData()) { + reader.setInput(data.getInputStream()); + } + } + + public void testSetInputNull() { + // Should just pass with no exceptions + ImageReader reader = createReader(); + assertNotNull(reader); + reader.setInput(null); + } + + public void testRead() throws IOException { + ImageReader reader = createReader(); + for (TestData data : getTestData()) { + // TODO: Is it required to call reset before setInput? + reader.setInput(data.getInputStream()); + + // TODO: Require count to match? +// System.out.println("reader.getNumImages(true): " + reader.getNumImages(true)); + + for (int i = 0; i < data.getImageCount(); i++) { + BufferedImage image = null; + try { + image = reader.read(i); + } + catch (Exception e) { + e.printStackTrace(); + fail(String.format("Image %s index %s could not be read: %s", data.getInput(), i, e)); + } + assertNotNull(String.format("Image %s index %s was null!", data.getInput(), i), image); + + assertEquals( + String.format("Image %s index %s has wrong width: %s", data.getInput(), i, image.getWidth()), + data.getDimension(i).width, + image.getWidth() + ); + assertEquals( + String.format("Image %s index %s has wrong height: %s", data.getInput(), i, image.getHeight()), + data.getDimension(i).height, image.getHeight() + ); + } + } + } + + public void testReadIndexNegative() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(-1); + fail("Read image with illegal index"); + } + catch (IndexOutOfBoundsException ignore) { + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void testReadIndexOutOfBounds() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(Integer.MAX_VALUE); // TODO: This might actually not throw exception... + fail("Read image with index out of bounds"); + } + catch (IndexOutOfBoundsException ignore) { + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void testReadNoInput() { + ImageReader reader = createReader(); + // Do not set input + + BufferedImage image = null; + try { + image = reader.read(0); + fail("Read image with no input"); + } + catch (IllegalStateException ignore) { + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void testReRead() throws IOException { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream(), false); // Explicitly allow repositioning, even though it's the default + + BufferedImage first = reader.read(0); + assertNotNull(first); + + BufferedImage second = reader.read(0); + assertNotNull(second); + + // TODO: These images should be exactly the same, but there's no equals for images + assertEquals(first.getType(), second.getType()); + assertEquals(first.getWidth(), second.getWidth()); + assertEquals(first.getHeight(), second.getHeight()); + } + + public void testReadIndexNegativeWithParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(-1, reader.getDefaultReadParam()); + fail("Read image with illegal index"); + } + catch (IndexOutOfBoundsException ignore) { + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void testReadIndexOutOfBoundsWithParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(99, reader.getDefaultReadParam()); + fail("Read image with index out of bounds"); + } + catch (IndexOutOfBoundsException ignore) { + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void testReadNoInputWithParam() { + ImageReader reader = createReader(); + // Do not set input + + BufferedImage image = null; + try { + image = reader.read(0, reader.getDefaultReadParam()); + fail("Read image with no input"); + } + catch (IllegalStateException ignore) { + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void testReadWithNewParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(0, new ImageReadParam()); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + data.getDimension(0).width, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + data.getDimension(0).height, image.getHeight()); + } + + public void testReadWithDefaultParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(0, reader.getDefaultReadParam()); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + data.getDimension(0).width, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + data.getDimension(0).height, image.getHeight()); + } + + public void testReadWithNullParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(0, null); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + data.getDimension(0).width, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + data.getDimension(0).height, image.getHeight()); + } + + public void testReadWithSizeParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + ImageReadParam param = reader.getDefaultReadParam(); + + if (param.canSetSourceRenderSize()) { + param.setSourceRenderSize(new Dimension(10, 10)); + + BufferedImage image = null; + try { + image = reader.read(0, param); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + 10, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + 10, image.getHeight()); + } + } + + public void testReadWithSubsampleParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + ImageReadParam param = reader.getDefaultReadParam(); + + param.setSourceSubsampling(5, 5, 0, 0); + + BufferedImage image = null; + try { + image = reader.read(0, param); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: ", + (double) data.getDimension(0).width / 5.0, image.getWidth(), 1.0); + assertEquals("Read image has wrong height: ", + (double) data.getDimension(0).height / 5.0, image.getHeight(), 1.0); + } + + public void testReadWithSourceRegionParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + ImageReadParam param = reader.getDefaultReadParam(); + + param.setSourceRegion(new Rectangle(0, 0, 10, 10)); + + BufferedImage image = null; + try { + image = reader.read(0, param); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + 10, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + 10, image.getHeight()); + } + + public void testReadWithSizeAndSourceRegionParam() { + // TODO: Is this test correct??? + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + ImageReadParam param = reader.getDefaultReadParam(); + + if (param.canSetSourceRenderSize()) { + Dimension size = data.getDimension(0); + size = new Dimension(size.width * 2, size.height * 2); + + param.setSourceRenderSize(size); + param.setSourceRegion(new Rectangle(0, 0, 10, 10)); + + BufferedImage image = null; + try { + image = reader.read(0, param); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + 20, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + 20, image.getHeight()); + } + } + + public void testReadWithSubsampleAndSourceRegionParam() { + // NOTE: The "standard" (com.sun.imageio.plugin.*) ImageReaders pass + // this test, so the test should be correct... + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + ImageReadParam param = reader.getDefaultReadParam(); + + param.setSourceSubsampling(2, 2, 0, 0); + param.setSourceRegion(new Rectangle(0, 0, 10, 10)); + + BufferedImage image = null; + try { + image = reader.read(0, param); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + 5, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + 5, image.getHeight()); + + } + + public void readAsRenderedImageIndexNegative() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(-1, reader.getDefaultReadParam()); + fail("Read image with illegal index"); + } + catch (IndexOutOfBoundsException expected) { + // Ignore + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void readAsRenderedImageIndexOutOfBounds() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + BufferedImage image = null; + try { + image = reader.read(11, reader.getDefaultReadParam()); + fail("Read image with index out of bounds"); + } + catch (IndexOutOfBoundsException expected) { + // Ignore + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void readAsRenderedImageNoInput() { + ImageReader reader = createReader(); + // Do not set input + + BufferedImage image = null; + try { + image = reader.read(0, reader.getDefaultReadParam()); + fail("Read image with no input"); + } + catch (IllegalStateException expected) { + // Ignore + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNull(image); + } + + public void readAsRenderedImage() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + RenderedImage image = null; + try { + image = reader.readAsRenderedImage(0, null); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + data.getDimension(0).width, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + data.getDimension(0).height, image.getHeight()); + } + + public void readAsRenderedImageWithDefaultParam() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + RenderedImage image = null; + try { + image = reader.readAsRenderedImage(0, reader.getDefaultReadParam()); + } + catch (IOException e) { + fail("Image could not be read: " + e); + } + assertNotNull("Image was null!", image); + assertEquals("Read image has wrong width: " + image.getWidth(), + data.getDimension(0).width, image.getWidth()); + assertEquals("Read image has wrong height: " + image.getHeight(), + data.getDimension(0).height, image.getHeight()); + } + + public void testGetDefaultReadParam() { + ImageReader reader = createReader(); + ImageReadParam param = reader.getDefaultReadParam(); + assertNotNull(param); + } + + public void testGetFormatName() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + String name = null; + try { + name = reader.getFormatName(); + } + catch (IOException e) { + fail(e.getMessage()); + } + assertNotNull(name); + } + + public void testGetMinIndex() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + int num = 0; + + try { + num = reader.getMinIndex(); + } + catch (IllegalStateException ignore) { + } + assertEquals(0, num); + } + + public void testGetMinIndexNoInput() { + ImageReader reader = createReader(); + int num = 0; + + try { + num = reader.getMinIndex(); + } + catch (IllegalStateException ignore) { + } + assertEquals(0, num); + } + + public void testGetNumImages() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + int num = -1; + try { + num = reader.getNumImages(false); + } + catch (IOException e) { + fail(e.getMessage()); + } + assertTrue(num == -1 || num > 0); + + try { + num = reader.getNumImages(true); + } + catch (IOException e) { + fail(e.getMessage()); + } + assertTrue(num > 0); + } + + public void testGetNumImagesNoInput() { + ImageReader reader = createReader(); + int num = -1; + + try { + num = reader.getNumImages(false); + } + catch (IllegalStateException ignore) { + } + catch (IOException e) { + fail(e.getMessage()); + } + assertEquals(-1, num); + + try { + num = reader.getNumImages(true); + fail("Should throw IllegalStateException"); + } + catch (IllegalStateException ignore) { + } + catch (IOException e) { + fail(e.getMessage()); + } + assertEquals(-1, num); + } + + public void testGetWidth() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + int width = 0; + try { + width = reader.getWidth(0); + } + catch (IOException e) { + fail("Could not read image width: " + e); + } + assertEquals("Wrong width reported", data.getDimension(0).width, width); + } + + public void testGetWidthIndexOutOfBounds() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + //int width = 0; + try { + /*width = */reader.getWidth(-1); + // NOTE: Some readers (like the com.sun.imageio stuff) ignores + // index in getWidth/getHeight for formats with only one image... + //assertEquals("Wrong width reported", data.getDimension().width, width); + } + catch (IndexOutOfBoundsException ignore) { + } + catch (IOException e) { + fail("Could not read image aspect ratio: " + e); + } + } + + public void testGetWidthNoInput() { + ImageReader reader = createReader(); + + int width = 0; + try { + width = reader.getWidth(0); + fail("Width read without imput"); + } + catch (IllegalStateException ignore) { + } + catch (IOException e) { + fail("Could not read image width: " + e); + } + assertEquals("Wrong width reported", 0, width); + } + + public void testGetHeight() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + int height = 0; + try { + height = reader.getHeight(0); + } + catch (IOException e) { + fail("Could not read image height: " + e); + } + assertEquals("Wrong height reported", data.getDimension(0).height, height); + } + + public void testGetHeightNoInput() { + ImageReader reader = createReader(); + + int height = 0; + try { + height = reader.getHeight(0); + fail("height read without imput"); + } + catch (IllegalStateException ignore) { + } + catch (IOException e) { + fail("Could not read image height: " + e); + } + assertEquals("Wrong height reported", 0, height); + } + + public void testGetHeightIndexOutOfBounds() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + //int height = 0; + try { + /*height =*/ reader.getHeight(-1); + // NOTE: Some readers (like the com.sun.imageio stuff) ignores + // index in getWidth/getHeight for formats with only one image... + //assertEquals("Wrong height reported", data.getDimension().height, height); + } + catch (IndexOutOfBoundsException ignore) { + } + catch (IOException e) { + fail("Could not read image height: " + e); + } + } + + public void testGetAspectratio() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + float aspect = 0f; + try { + aspect = reader.getAspectRatio(0); + } + catch (IOException e) { + fail("Could not read image aspectratio" + e); + } + Dimension d = data.getDimension(0); + assertEquals("Wrong aspect aspectratio", d.getWidth() / d.getHeight(), aspect, 0.001); + } + + public void testGetAspectratioNoInput() { + ImageReader reader = createReader(); + + float aspect = 0f; + try { + aspect = reader.getAspectRatio(0); + fail("aspect read without imput"); + } + catch (IllegalStateException ignore) { + } + catch (IOException e) { + fail("Could not read image aspectratio" + e); + } + assertEquals("Wrong aspect aspectratio", 0f, aspect, 0f); + } + + public void testGetAspectratioIndexOutOfBounds() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + //float aspectratio = 0f; + try { + // NOTE: Some readers (like the com.sun.imageio stuff) ignores + // index in getWidth/getHeight for formats with only one image... + /*aspectratio =*/ reader.getAspectRatio(-1); + //assertEquals("Wrong aspectratio aspectratio", data.getDimension().width / (float) data.getDimension().height, aspectratio, 0f); + } + catch (IndexOutOfBoundsException ignore) { + } + catch (IOException e) { + fail("Could not read image aspectratio" + e); + } + } + + public void testDispose() { + // TODO: Implement + } + + public void testAddIIOReadProgressListener() { + ImageReader reader = createReader(); + Mock mockListener = new Mock(IIOReadProgressListener.class); + reader.addIIOReadProgressListener((IIOReadProgressListener) mockListener.proxy()); + } + + public void testAddIIOReadProgressListenerNull() { + ImageReader reader = createReader(); + reader.addIIOReadProgressListener(null); + } + + public void testAddIIOReadProgressListenerCallbacks() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + Mock mockListener = new Mock(IIOReadProgressListener.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); + + reader.addIIOReadProgressListener((IIOReadProgressListener) mockListener.proxy()); + + try { + reader.read(0); + } + catch (IOException e) { + fail("Could not read image"); + } + + // At least imageStarted and imageComplete, plus any number of imageProgress + mockListener.verify(); + } + + public void testMultipleAddIIOReadProgressListenerCallbacks() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + Mock mockListener = new Mock(IIOReadProgressListener.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(IIOReadProgressListener.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(IIOReadProgressListener.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); + + + reader.addIIOReadProgressListener((IIOReadProgressListener) mockListener.proxy()); + reader.addIIOReadProgressListener((IIOReadProgressListener) mockListenerToo.proxy()); + reader.addIIOReadProgressListener((IIOReadProgressListener) mockListenerThree.proxy()); + + try { + reader.read(0); + } + catch (IOException e) { + fail("Could not read image"); + } + + // At least imageStarted and imageComplete, plus any number of imageProgress + mockListener.verify(); + mockListenerToo.verify(); + mockListenerThree.verify(); + } + + public void testRemoveIIOReadProgressListenerNull() { + ImageReader reader = createReader(); + reader.removeIIOReadProgressListener(null); + } + + public void testRemoveIIOReadProgressListenerNone() { + ImageReader reader = createReader(); + Mock mockListener = new Mock(IIOReadProgressListener.class); + reader.removeIIOReadProgressListener((IIOReadProgressListener) mockListener.proxy()); + } + + public void testRemoveIIOReadProgressListener() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + Mock mockListener = new Mock(IIOReadProgressListener.class); + IIOReadProgressListener listener = (IIOReadProgressListener) mockListener.proxy(); + reader.addIIOReadProgressListener(listener); + reader.removeIIOReadProgressListener(listener); + + try { + reader.read(0); + } + catch (IOException e) { + fail("Could not read image"); + } + + // Should not have called any methods... + mockListener.verify(); + } + + public void testRemoveIIOReadProgressListenerMultiple() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + Mock mockListener = new Mock(IIOReadProgressListener.class, "Listener1"); + IIOReadProgressListener listener = (IIOReadProgressListener) mockListener.proxy(); + reader.addIIOReadProgressListener(listener); + + Mock mockListenerToo = new Mock(IIOReadProgressListener.class, "Listener2"); + mockListenerToo.expects(once()).method("imageStarted").with(eq(reader), eq(0)); + mockListenerToo.stubs().method("imageProgress").withAnyArguments(); + mockListenerToo.expects(once()).method("imageComplete").with(eq(reader)); + IIOReadProgressListener listenerToo = (IIOReadProgressListener) mockListenerToo.proxy(); + reader.addIIOReadProgressListener(listenerToo); + + reader.removeIIOReadProgressListener(listener); + + try { + reader.read(0); + } + catch (IOException e) { + fail("Could not read image"); + } + + // Should not have called any methods... + mockListener.verify(); + mockListenerToo.verify(); + } + + public void testRemoveAllIIOReadProgressListeners() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + Mock mockListener = new Mock(IIOReadProgressListener.class); + reader.addIIOReadProgressListener((IIOReadProgressListener) mockListener.proxy()); + + reader.removeAllIIOReadProgressListeners(); + + try { + reader.read(0); + } + catch (IOException e) { + fail("Could not read image"); + } + + // Should not have called any methods... + mockListener.verify(); + } + + public void testRemoveAllIIOReadProgressListenersMultiple() { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + Mock mockListener = new Mock(IIOReadProgressListener.class); + reader.addIIOReadProgressListener((IIOReadProgressListener) mockListener.proxy()); + + Mock mockListenerToo = new Mock(IIOReadProgressListener.class); + reader.addIIOReadProgressListener((IIOReadProgressListener) mockListenerToo.proxy()); + + reader.removeAllIIOReadProgressListeners(); + + try { + reader.read(0); + } + catch (IOException e) { + fail("Could not read image"); + } + + // Should not have called any methods... + mockListener.verify(); + mockListenerToo.verify(); + } + + public void testAbort() { + final ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + Mock mockListener = new Mock(IIOReadProgressListener.class, "Progress1"); + mockListener.stubs().method("imageStarted").withAnyArguments(); + mockListener.stubs().method("imageProgress").withAnyArguments(); + mockListener.expects(once()).method("readAborted").with(eq(reader)); + mockListener.stubs().method("imageComplete").withAnyArguments(); + IIOReadProgressListener listener = (IIOReadProgressListener) mockListener.proxy(); + reader.addIIOReadProgressListener(listener); + + Mock mockListenerToo = new Mock(IIOReadProgressListener.class, "Progress2"); + mockListenerToo.stubs().method("imageStarted").withAnyArguments(); + mockListenerToo.stubs().method("imageProgress").withAnyArguments(); + mockListenerToo.expects(once()).method("readAborted").with(eq(reader)); + mockListenerToo.stubs().method("imageComplete").withAnyArguments(); + IIOReadProgressListener listenerToo = (IIOReadProgressListener) mockListenerToo.proxy(); + reader.addIIOReadProgressListener(listenerToo); + + // Create a listener that just makes the reader abort immediately... + Mock abortingListener = new Mock(IIOReadProgressListener.class, "Aborter"); + abortingListener.stubs().method("readAborted").withAnyArguments(); + abortingListener.stubs().method("imageComplete").withAnyArguments(); + Stub abort = new Stub() { + public Object invoke(Invocation pInvocation) throws Throwable { + reader.abort(); + return null; + } + + public StringBuffer describeTo(StringBuffer pStringBuffer) { + pStringBuffer.append("aborting"); + return pStringBuffer; + } + }; + abortingListener.stubs().method("imageProgress").will(abort); + abortingListener.stubs().method("imageStarted").will(abort); + + reader.addIIOReadProgressListener((IIOReadProgressListener) abortingListener.proxy()); + + try { + reader.read(0); + } + catch (IOException e) { + fail("Could not read image: " + e.getMessage() ); + } + + mockListener.verify(); + mockListenerToo.verify(); + } + + public void testGetTypeSpecifiers() throws IOException { + final ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + ImageTypeSpecifier rawType = reader.getRawImageType(0); + assertNotNull(rawType); + + Iterator types = reader.getImageTypes(0); + + assertNotNull(types); + assertTrue(types.hasNext()); + + // TODO: This might fail even though the specifiers are obviously equal, if the + // color spaces they use are not the SAME instance, as ColorSpace uses identity equals + // and Interleaved ImageTypeSpecifiers are only equal if color spaces are equal... + boolean rawFound = false; + while (types.hasNext()) { + ImageTypeSpecifier type = types.next(); + if (type.equals(rawType)) { + rawFound = true; + break; + } + } + + assertTrue("ImageTypeSepcifier from getRawImageType should be in the iterator from getImageTypes", rawFound); + } + + public void testSetDestination() throws IOException { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + ImageReadParam param = reader.getDefaultReadParam(); + Iterator types = reader.getImageTypes(0); + while (types.hasNext()) { + ImageTypeSpecifier type = types.next(); + + BufferedImage destination = type.createBufferedImage(50, 50); + param.setDestination(destination); + + BufferedImage result = reader.read(0, param); + + assertSame(destination, result); + } + } + + public void testSetDestinationRaw() throws IOException { + ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + ImageReadParam param = reader.getDefaultReadParam(); + + ImageTypeSpecifier type = reader.getRawImageType(0); + BufferedImage destination = type.createBufferedImage(reader.getWidth(0), reader.getHeight(0)); + param.setDestination(destination); + + BufferedImage result = reader.read(0, param); + + assertSame(destination, result); + } + + // TODO: This test is foobar.. + public void testSetDestinationIllegal() throws IOException { + // TODO: Test that the reader throws IIOException if given an illegal destination + final ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + Iterator types = reader.getImageTypes(0); + + ImageReadParam param = reader.getDefaultReadParam(); + // TODO: Should either be a type from image type specifiers or throw IIOException in read + BufferedImage destination = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB); + param.setDestination(destination); + + try { + reader.read(0, param); + fail("Expected to throw exception with wrong type specifier"); + } + catch (IIOException e) { + assertTrue(e.getMessage().toLowerCase().contains("type")); + } + } + + // TODO: Test dest offset + destination set? + public void testSetDestinationOffset() throws IOException { + final ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + ImageReadParam param = reader.getDefaultReadParam(); + Point point = new Point(10, 10); + param.setDestinationOffset(point); + + BufferedImage image = reader.read(0, param); + + assertNotNull(image); + assertEquals(reader.getWidth(0) + point.x, image.getWidth()); + assertEquals(reader.getHeight(0) + point.y, image.getHeight()); + } + + public void testSetDestinationOffsetNull() throws IOException { + final ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + ImageReadParam param = reader.getDefaultReadParam(); + try { + param.setDestinationOffset(null); + fail("Null offset not allowed"); + } + catch (IllegalArgumentException e) { + assertTrue(e.getMessage().toLowerCase().contains("offset")); + } + } + + public void testSetDestinationType() throws IOException { + final ImageReader reader = createReader(); + TestData data = getTestData().get(0); + reader.setInput(data.getInputStream()); + + ImageReadParam param = reader.getDefaultReadParam(); + + Iterator types = reader.getImageTypes(0); + while (types.hasNext()) { + ImageTypeSpecifier type = types.next(); + param.setDestinationType(type); + + BufferedImage result = reader.read(0, param); + + assertEquals(type.getColorModel(), result.getColorModel()); + +// assertEquals(type.getSampleModel(), result.getSampleModel()); + SampleModel expectedModel = type.getSampleModel(); + SampleModel resultModel = result.getSampleModel(); + + assertEquals(expectedModel.getDataType(), resultModel.getDataType()); + assertEquals(expectedModel.getNumBands(), resultModel.getNumBands()); + assertEquals(expectedModel.getNumDataElements(), resultModel.getNumDataElements()); + assertTrue(Arrays.equals(expectedModel.getSampleSize(), resultModel.getSampleSize())); + assertEquals(expectedModel.getTransferType(), resultModel.getTransferType()); + for (int i = 0; i < expectedModel.getNumBands(); i++) { + assertEquals(expectedModel.getSampleSize(i), resultModel.getSampleSize(i)); + } + } + } + + public void testSetDestinationTypeIllegal() throws IOException { + throw new UnsupportedOperationException("Method testSetDestinationTypeIllegal not implemented"); // TODO: Implement + } + + public void testSetDestinationBands() throws IOException { + throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement + } + + public void testSetSourceBands() throws IOException { + throw new UnsupportedOperationException("Method testSetDestinationBands not implemented"); // TODO: Implement + } + + + protected URL getClassLoaderResource(final String pName) { + return getClass().getResource(pName); + } + + static final protected class TestData { + private final Object mInput; + private final List mSizes; + private final List mImages; + + public TestData(final Object pInput, final Dimension... pSizes) { + this(pInput, Arrays.asList(pSizes), null); + } + + public TestData(final Object pInput, final BufferedImage... pImages) { + this(pInput, null, Arrays.asList(pImages)); + } + + public TestData(final Object pInput, final List pSizes, final List pImages) { + if (pInput == null) { + throw new IllegalArgumentException("input == null"); + } + + mSizes = new ArrayList(); + mImages = new ArrayList(); + + List sizes = pSizes; + if (sizes == null) { + sizes = new ArrayList(); + if (pImages != null) { + for (BufferedImage image : pImages) { + sizes.add(new Dimension(image.getWidth(), image.getHeight())); + } + } + else { + throw new IllegalArgumentException("Need either size or image"); + } + } + else if (pImages != null) { + if (pImages.size() != pSizes.size()) { + throw new IllegalArgumentException("Size parameter and image size differs"); + } + for (int i = 0; i < sizes.size(); i++) { + if (!new Dimension(pImages.get(i).getWidth(), pImages.get(i).getHeight()).equals(sizes.get(i))) { + throw new IllegalArgumentException("Size parameter and image size differs"); + } + + } + } + + mSizes.addAll(sizes); + if (pImages != null) { + mImages.addAll(pImages); + } + + mInput = pInput; + } + + public Object getInput() { + return mInput; + } + + public ImageInputStream getInputStream() { + try { + ImageInputStream stream = ImageIO.createImageInputStream(mInput); + assertNotNull("Could not create ImageInputStream for input: " + mInput, stream); + return stream; + } + catch (IOException e) { + fail("Could not create ImageInputStream for input: " + mInput + + "\n caused by: " + e.getMessage()); + } + return null; + } + + public int getImageCount() { + return mSizes.size(); + } + + public Dimension getDimension(final int pIndex) { + return mSizes.get(pIndex); + } + + public BufferedImage getImage(final int pIndex) { + return mImages.get(pIndex); + } + + @Override + public String toString() { + return getClass().getSimpleName() + ": " + String.valueOf(mInput); + } + } +} diff --git a/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageWriterAbstractTestCase.java b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageWriterAbstractTestCase.java new file mode 100755 index 00000000..13373a4e --- /dev/null +++ b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/ImageWriterAbstractTestCase.java @@ -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 Harald Kuhr + * @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(); + } + +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifierTestCase.java b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifierTestCase.java new file mode 100755 index 00000000..3b24dfa9 --- /dev/null +++ b/twelvemonkeys-imageio/core/src/test/java/com/twelvemonkeys/imageio/util/IndexedImageTypeSpecifierTestCase.java @@ -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 Harald Kuhr + * @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)); + } +} diff --git a/twelvemonkeys-imageio/core/src/test/resources/Thumbs-camera.db b/twelvemonkeys-imageio/core/src/test/resources/Thumbs-camera.db new file mode 100755 index 00000000..1adedbb8 Binary files /dev/null and b/twelvemonkeys-imageio/core/src/test/resources/Thumbs-camera.db differ diff --git a/twelvemonkeys-imageio/core/todo.txt b/twelvemonkeys-imageio/core/todo.txt new file mode 100755 index 00000000..c2644570 --- /dev/null +++ b/twelvemonkeys-imageio/core/todo.txt @@ -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 \ No newline at end of file diff --git a/twelvemonkeys-imageio/ico/license.txt b/twelvemonkeys-imageio/ico/license.txt new file mode 100755 index 00000000..2d8ee79c --- /dev/null +++ b/twelvemonkeys-imageio/ico/license.txt @@ -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. \ No newline at end of file diff --git a/twelvemonkeys-imageio/ico/pom.xml b/twelvemonkeys-imageio/ico/pom.xml new file mode 100755 index 00000000..0b696731 --- /dev/null +++ b/twelvemonkeys-imageio/ico/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + com.twelvemonkeys.imageio + twelvemonkeys-imageio-ico + 2.1 + TwelveMonkeys ImageIO ICO plugin + ImageIO plugin for Windows Icon (ICO) and Cursor (CUR) format. + + + twelvemonkeys-imageio + com.twelvemonkeys + 2.1 + + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + tests + + + \ No newline at end of file diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapDescriptor.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapDescriptor.java new file mode 100755 index 00000000..36c67748 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapDescriptor.java @@ -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 Harald Kuhr + * @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(); + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapIndexed.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapIndexed.java new file mode 100755 index 00000000..bd73dd75 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapIndexed.java @@ -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 Harald Kuhr + * @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 properties = null; + if (mEntry instanceof DirectoryEntry.CUREntry) { + DirectoryEntry.CUREntry entry = (DirectoryEntry.CUREntry) mEntry; + properties = new Hashtable(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; + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapMask.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapMask.java new file mode 100755 index 00000000..e5e11c1c --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapMask.java @@ -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 Harald Kuhr + * @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(); + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapRGB.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapRGB.java new file mode 100755 index 00000000..774caf48 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapRGB.java @@ -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 Harald Kuhr + * @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; + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapUnsupported.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapUnsupported.java new file mode 100755 index 00000000..73d7be5b --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapUnsupported.java @@ -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 Harald Kuhr + * @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); + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java new file mode 100755 index 00000000..77a8abd6 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java @@ -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 Harald Kuhr + * @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 hotspot location for the cursor. + * + * @param pImageIndex the index of the cursor in the current input. + * @return the hotspot 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(); + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java new file mode 100755 index 00000000..34dab925 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java @@ -0,0 +1,77 @@ +/* + * 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.ImageReader; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; +import java.util.Locale; + +/** + * CURImageReaderSpi + * + * @author Harald Kuhr + * @version $Id: CURImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$ + */ +public class CURImageReaderSpi extends ImageReaderSpi { + + public CURImageReaderSpi() { + super( + "TwelveMonkeys", + "2.1", + 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"; + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIB.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIB.java new file mode 100755 index 00000000..3a02d970 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIB.java @@ -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 Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: DIB.java,v 1.0 Apr 8, 2008 1:43:04 PM haraldk Exp$ + * + * @see BMP file format (Wikipedia) + * @see ICO file format (Wikipedia) + */ +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; +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBHeader.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBHeader.java new file mode 100755 index 00000000..af4ef23f --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBHeader.java @@ -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 Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: DIBHeader.java,v 1.0 May 5, 2009 10:45:31 AM haraldk Exp$ + * @see BMP file format (Wikipedia) + */ +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. + *

+ * + * @author Harald Kuhr + * @version $Id: DIBHeader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$ + * @see BMP file format (Wikipedia) + */ + 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(); + } + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/Directory.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/Directory.java new file mode 100755 index 00000000..9cafc0c4 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/Directory.java @@ -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 + *

+ * + * @author Harald Kuhr + * @version $Id: Directory.java,v 1.0 25.feb.2006 00:29:44 haku Exp$ + */ +class Directory { + private final List 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); + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java new file mode 100755 index 00000000..163c3acd --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java @@ -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 Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: DirectoryEntry.java,v 1.0 Apr 4, 2009 4:29:53 PM haraldk Exp$ + * @see Wikipedia + */ +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 { + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java new file mode 100755 index 00000000..9e3503fb --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java @@ -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 ecoded icons. + *

+ * + * @author Harald Kuhr + * @version $Id: ICOImageReader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$ + * + * @see BMP file format (Wikipedia) + * @see ICO file format (Wikipedia) + */ +// 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 +// MSDN +// 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 mHeaders = new WeakHashMap(); + private Map mDescriptors = new WeakWeakMap(); + + 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 getImageTypes(final int pImageIndex) throws IOException { + DirectoryEntry entry = getEntry(pImageIndex); + + // NOTE: Delegate to PNG reader + if (isPNG(entry)) { + return getImageTypesPNG(entry); + } + + List types = new ArrayList(); + 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 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 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: Currently, we have a memory leak, as the values refer to the keys... + BitmapDescriptor descriptor = mDescriptors.get(pEntry); + + if (!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 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); + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java new file mode 100755 index 00000000..c9dcb90b --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java @@ -0,0 +1,94 @@ +/* + * 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.ImageReader; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; +import java.util.Locale; + +/** + * ICOImageReaderSpi + * + * @author Harald Kuhr + * @version $Id: ICOImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$ + */ +public class ICOImageReaderSpi extends ImageReaderSpi { + + public ICOImageReaderSpi() { + super( + "TwelveMonkeys", + "2.1", + 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"; + } +} diff --git a/twelvemonkeys-imageio/ico/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/twelvemonkeys-imageio/ico/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi new file mode 100755 index 00000000..e06c9b76 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi @@ -0,0 +1,2 @@ +com.twelvemonkeys.imageio.plugins.ico.ICOImageReaderSpi +com.twelvemonkeys.imageio.plugins.ico.CURImageReaderSpi \ No newline at end of file diff --git a/twelvemonkeys-imageio/ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java b/twelvemonkeys-imageio/ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java new file mode 100755 index 00000000..d8aea766 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java @@ -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 Harald Kuhr + * @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 { + protected List 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 getReaderClass() { + return CURImageReader.class; + } + + protected List getFormatNames() { + return Arrays.asList("cur"); + } + + protected List getSuffixes() { + return Arrays.asList("cur"); + } + + protected List 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 +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderTestCase.java b/twelvemonkeys-imageio/ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderTestCase.java new file mode 100755 index 00000000..72036311 --- /dev/null +++ b/twelvemonkeys-imageio/ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderTestCase.java @@ -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 Harald Kuhr + * @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 { + protected List 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 getReaderClass() { + return ICOImageReader.class; + } + + protected List getFormatNames() { + return Arrays.asList("ico"); + } + + protected List getSuffixes() { + return Arrays.asList("ico"); + } + + protected List getMIMETypes() { + return Arrays.asList("image/vnd.microsoft.icon", "image/ico", "image/x-icon"); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/ico/src/test/resources/cur/hand.cur b/twelvemonkeys-imageio/ico/src/test/resources/cur/hand.cur new file mode 100755 index 00000000..db7ad5ae Binary files /dev/null and b/twelvemonkeys-imageio/ico/src/test/resources/cur/hand.cur differ diff --git a/twelvemonkeys-imageio/ico/src/test/resources/cur/zoom.cur b/twelvemonkeys-imageio/ico/src/test/resources/cur/zoom.cur new file mode 100755 index 00000000..f4be2a7d Binary files /dev/null and b/twelvemonkeys-imageio/ico/src/test/resources/cur/zoom.cur differ diff --git a/twelvemonkeys-imageio/ico/src/test/resources/ico/JavaCup.ico b/twelvemonkeys-imageio/ico/src/test/resources/ico/JavaCup.ico new file mode 100755 index 00000000..c0da7d76 Binary files /dev/null and b/twelvemonkeys-imageio/ico/src/test/resources/ico/JavaCup.ico differ diff --git a/twelvemonkeys-imageio/ico/src/test/resources/ico/colors.ico b/twelvemonkeys-imageio/ico/src/test/resources/ico/colors.ico new file mode 100755 index 00000000..7529c66e Binary files /dev/null and b/twelvemonkeys-imageio/ico/src/test/resources/ico/colors.ico differ diff --git a/twelvemonkeys-imageio/ico/src/test/resources/ico/down.ico b/twelvemonkeys-imageio/ico/src/test/resources/ico/down.ico new file mode 100755 index 00000000..991264fd Binary files /dev/null and b/twelvemonkeys-imageio/ico/src/test/resources/ico/down.ico differ diff --git a/twelvemonkeys-imageio/ico/src/test/resources/ico/favicon.ico b/twelvemonkeys-imageio/ico/src/test/resources/ico/favicon.ico new file mode 100755 index 00000000..161bcf78 Binary files /dev/null and b/twelvemonkeys-imageio/ico/src/test/resources/ico/favicon.ico differ diff --git a/twelvemonkeys-imageio/ico/src/test/resources/ico/joypad.ico b/twelvemonkeys-imageio/ico/src/test/resources/ico/joypad.ico new file mode 100755 index 00000000..eb634169 Binary files /dev/null and b/twelvemonkeys-imageio/ico/src/test/resources/ico/joypad.ico differ diff --git a/twelvemonkeys-imageio/ico/todo.txt b/twelvemonkeys-imageio/ico/todo.txt new file mode 100755 index 00000000..173c7fa3 --- /dev/null +++ b/twelvemonkeys-imageio/ico/todo.txt @@ -0,0 +1 @@ +- Support all DIB formats? diff --git a/twelvemonkeys-imageio/iff/license.txt b/twelvemonkeys-imageio/iff/license.txt new file mode 100755 index 00000000..2d8ee79c --- /dev/null +++ b/twelvemonkeys-imageio/iff/license.txt @@ -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. \ No newline at end of file diff --git a/twelvemonkeys-imageio/iff/pom.xml b/twelvemonkeys-imageio/iff/pom.xml new file mode 100755 index 00000000..67829cbc --- /dev/null +++ b/twelvemonkeys-imageio/iff/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + com.twelvemonkeys.imageio + twelvemonkeys-imageio-iff + 2.1 + TwelveMonkeys ImageIO IFF plugin + + ImageIO plugin for Amiga/Electronic Arts Interchange Filed Format (IFF) + type ILBM and PBM format. + + + + twelvemonkeys-imageio + com.twelvemonkeys + 2.1 + + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + tests + + + \ No newline at end of file diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/BMHDChunk.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/BMHDChunk.java new file mode 100755 index 00000000..c02cb3a8 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/BMHDChunk.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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 + "}"; + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/BODYChunk.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/BODYChunk.java new file mode 100755 index 00000000..9cbb9497 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/BODYChunk.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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"); + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/CAMGChunk.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/CAMGChunk.java new file mode 100755 index 00000000..7b906951 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/CAMGChunk.java @@ -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.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + * CAMGChunk + *

+ * + * @author Harald Kuhr + * @version $Id: CAMGChunk.java,v 1.0 28.feb.2006 02:10:07 haku Exp$ + */ +class CAMGChunk extends IFFChunk { + + // #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") + "}"; + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/CMAPChunk.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/CMAPChunk.java new file mode 100755 index 00000000..b9280135 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/CMAPChunk.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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); + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/GRABChunk.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/GRABChunk.java new file mode 100755 index 00000000..bb4cc03b --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/GRABChunk.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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 + "}"; + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/GenericChunk.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/GenericChunk.java new file mode 100755 index 00000000..0f14ff90 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/GenericChunk.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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 ? "" : "...") + "\"}"; + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFF.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFF.java new file mode 100755 index 00000000..504d5aa9 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFF.java @@ -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. + *

+ * + * @author Harald Kuhr + * @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'; +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFChunk.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFChunk.java new file mode 100755 index 00000000..c5517611 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFChunk.java @@ -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 + *

+ * + * @author Harald Kuhr + * @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)"; + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java new file mode 100755 index 00000000..f7d435f4 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java @@ -0,0 +1,693 @@ +/* + * 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. + *

+ * 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. + *

+ * 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}. + *

+ * 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 Wikipedia: HAM + * for more information. + *
+ * EHB palette is expanded to an {@link IndexColorModel} with 64 entries. + * See Wikipedia: EHB + * for more information. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: IFFImageReader.java,v 1.0 29.aug.2004 20:26:58 haku Exp $ + * @see Wikipedia: IFF + * @see Wikipedia: IFF ILBM + */ +public class IFFImageReader extends ImageReaderBase { + + 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) + " lenght: " + 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 { + // 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 getImageTypes(int pIndex) throws IOException { + init(pIndex); + + List 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()) { + IndexColorModel cm = mColorMap.getIndexColorModel(); + if (cm != null) { + specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(cm); + break; + } + else { + specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY); + break; + } + } + 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("Bit depth not implemented: " + mHeader.mBitplanes); + } + return specifier; + } + + private void readBody(ImageReadParam pParam) throws IOException { + mImageInput.seek(mBodyStart); // 8 for the header before length in stream + 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(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); + } + + int planeWidth = (width + 7) / 8; + 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++) { + try { + readPlaneData(pInput, planeData, p * planeWidth, planeWidth); + } + catch (IOException e) { + // TODO: Add warning? Probbably a bug somewhere, should not catch! + } + } + + // 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; + int planePos = 0; + for (int i = 0; i < planeWidth; i++) { + IFFUtil.bitRotateCW(planeData, planePos, planeWidth, row, pixelPos, 1); + pixelPos += 8; + planePos++; + } + + if (isHAM()) { + hamToRGB(row, pModel, data, 0); + } + else { + raster.setDataElements(0, 0, width, 1, row); + } + } + else /*if (mType == IFFImageReader.TYPE_PBM)*/ { + // TODO: Arraycopy might not be neccessary, 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); + } + + 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()); + + int planeWidth = (width + 7) / 8; + + 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); + 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; + int planePos = 0; + for (int i = 0; i < planeWidth; i++) { + IFFUtil.bitRotateCW(planeData, planePos, planeWidth, data, off + pixelPos * channels, channels); + pixelPos += 8; + planePos++; + } + } + else /*if (mType == IFFImageReader.TYPE_PBM)*/ { + System.arraycopy(planeData, 0, data, srcY * 8 * planeWidth, planeWidth); + } + } + + 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: + if (mByteRunStream == null) { + mByteRunStream = new DataInputStream(new DecoderStream( + IIOUtil.createStreamAdapter(pInput, mBody.mChunkLength), + new PackBitsDecoder(true) + )); + } + mByteRunStream.readFully(pData, pOffset, pPlaneWidth); + break; + default: + throw new IIOException("Unknown compression type: " + 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(new IFFImageReaderSpi()); + +// 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, ""); + } + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReaderSpi.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReaderSpi.java new file mode 100755 index 00000000..c256e4a6 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReaderSpi.java @@ -0,0 +1,114 @@ +/* + * 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.ImageReader; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; +import java.util.Locale; + +/** + * IFFImageReaderSpi + *

+ * + * @author Harald Kuhr + * @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 IFFImageReaderSpi + */ + public IFFImageReaderSpi() { + super( + "TwelveMonkeys", + "2.0", + 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; + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageWriter.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageWriter.java new file mode 100755 index 00000000..eacb950a --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageWriter.java @@ -0,0 +1,269 @@ +/* + * 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 allmost all image software for the Amiga computer. + *

+ * + * @author Harald Kuhr + * @version $Id: IFFImageWriter.java,v 1.0 02.mar.2006 13:32:30 haku Exp$ + * + * @see Wikipedia: IFF + * @see Wikipedia: IFF ILBM + */ +public class IFFImageWriter extends ImageWriterBase { + + private static final byte[] ANNO_DATA = "Written by TwelveMonkeys IFFImageWriter 1.0 for Java (javax.imageio).".getBytes(); + + 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: 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 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]; + + // 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 + GenericChunk anno = new GenericChunk(IFFUtil.toInt("ANNO".getBytes()), ANNO_DATA); + + 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); + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageWriterSpi.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageWriterSpi.java new file mode 100755 index 00000000..bdc52118 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageWriterSpi.java @@ -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.ImageTypeSpecifier; +import javax.imageio.ImageWriter; +import javax.imageio.spi.ImageWriterSpi; +import java.io.IOException; +import java.util.Locale; + +/** + * IFFImageWriterSpi + *

+ * + * @author Harald Kuhr + * @version $Id: IFFImageWriterSpi.java,v 1.0 02.mar.2006 19:21:05 haku Exp$ + */ +public class IFFImageWriterSpi extends ImageWriterSpi { + + /** + * Creates an IFFImageWriterSpi + */ + public IFFImageWriterSpi() { + super( + "TwelveMonkeys", + "$Revision: 1.0 $", + 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(ImageTypeSpecifier pType) { + // TODO: Probably can't store 16 bit types etc... + 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"; + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFUtil.java b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFUtil.java new file mode 100755 index 00000000..41e5fe9d --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFUtil.java @@ -0,0 +1,255 @@ +/* + * 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 + *

+ * 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 + * + * @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 + * @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))}); + } +} diff --git a/twelvemonkeys-imageio/iff/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/twelvemonkeys-imageio/iff/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi new file mode 100755 index 00000000..5086e73a --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi @@ -0,0 +1 @@ +com.twelvemonkeys.imageio.plugins.iff.IFFImageReaderSpi diff --git a/twelvemonkeys-imageio/iff/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi b/twelvemonkeys-imageio/iff/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi new file mode 100755 index 00000000..22f2491d --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi @@ -0,0 +1 @@ +com.twelvemonkeys.imageio.plugins.iff.IFFImageWriterSpi \ No newline at end of file diff --git a/twelvemonkeys-imageio/iff/src/test/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReaderTestCase.java b/twelvemonkeys-imageio/iff/src/test/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReaderTestCase.java new file mode 100755 index 00000000..2de33450 --- /dev/null +++ b/twelvemonkeys-imageio/iff/src/test/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReaderTestCase.java @@ -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 Harald Kuhr + * @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 { + // TODO: Need test for IFF PBM + protected List 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 - Passes tests, but visuals are trashed. Have other HAM8 files that are ok + new TestData(getClassLoaderResource("/iff/A4000T_HAM8.IFF"), new Dimension(628, 512)), // ham8 + // 8 color indexed - Passes tests, but trashed. Must be something special with these images + 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 + // Breaks completely... Could be bug in the packbits decoder? + new TestData(getClassLoaderResource("/iff/Abyss.iff"), new Dimension(320, 400)) + ); + } + + protected ImageReaderSpi createProvider() { + return new IFFImageReaderSpi(); + } + + protected Class getReaderClass() { + return IFFImageReader.class; + } + + protected List getFormatNames() { + return Arrays.asList("iff"); + } + + protected List getSuffixes() { + return Arrays.asList("iff", "ilbm", "ham", "ham8", "lbm"); + } + + protected List getMIMETypes() { + return Arrays.asList("image/iff", "image/x-iff"); + } +} diff --git a/twelvemonkeys-imageio/iff/src/test/resources/iff/A4000T_HAM6.IFF b/twelvemonkeys-imageio/iff/src/test/resources/iff/A4000T_HAM6.IFF new file mode 100755 index 00000000..d97fb46a Binary files /dev/null and b/twelvemonkeys-imageio/iff/src/test/resources/iff/A4000T_HAM6.IFF differ diff --git a/twelvemonkeys-imageio/iff/src/test/resources/iff/A4000T_HAM8.IFF b/twelvemonkeys-imageio/iff/src/test/resources/iff/A4000T_HAM8.IFF new file mode 100755 index 00000000..1f24bf42 Binary files /dev/null and b/twelvemonkeys-imageio/iff/src/test/resources/iff/A4000T_HAM8.IFF differ diff --git a/twelvemonkeys-imageio/iff/src/test/resources/iff/Abyss.iff b/twelvemonkeys-imageio/iff/src/test/resources/iff/Abyss.iff new file mode 100755 index 00000000..4540de22 Binary files /dev/null and b/twelvemonkeys-imageio/iff/src/test/resources/iff/Abyss.iff differ diff --git a/twelvemonkeys-imageio/iff/src/test/resources/iff/AmigaAmiga.iff b/twelvemonkeys-imageio/iff/src/test/resources/iff/AmigaAmiga.iff new file mode 100755 index 00000000..f96e69f1 Binary files /dev/null and b/twelvemonkeys-imageio/iff/src/test/resources/iff/AmigaAmiga.iff differ diff --git a/twelvemonkeys-imageio/iff/src/test/resources/iff/AmigaBig.iff b/twelvemonkeys-imageio/iff/src/test/resources/iff/AmigaBig.iff new file mode 100755 index 00000000..0ee086e5 Binary files /dev/null and b/twelvemonkeys-imageio/iff/src/test/resources/iff/AmigaBig.iff differ diff --git a/twelvemonkeys-imageio/iff/src/test/resources/iff/survivor.iff b/twelvemonkeys-imageio/iff/src/test/resources/iff/survivor.iff new file mode 100755 index 00000000..74a5b686 Binary files /dev/null and b/twelvemonkeys-imageio/iff/src/test/resources/iff/survivor.iff differ diff --git a/twelvemonkeys-imageio/iff/src/test/resources/iff/test.iff b/twelvemonkeys-imageio/iff/src/test/resources/iff/test.iff new file mode 100755 index 00000000..7b6cdd2b Binary files /dev/null and b/twelvemonkeys-imageio/iff/src/test/resources/iff/test.iff differ diff --git a/twelvemonkeys-imageio/iff/todo.txt b/twelvemonkeys-imageio/iff/todo.txt new file mode 100755 index 00000000..5fc2b2d1 --- /dev/null +++ b/twelvemonkeys-imageio/iff/todo.txt @@ -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: \ No newline at end of file diff --git a/twelvemonkeys-imageio/jmagick/license.txt b/twelvemonkeys-imageio/jmagick/license.txt new file mode 100755 index 00000000..2d8ee79c --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/license.txt @@ -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. \ No newline at end of file diff --git a/twelvemonkeys-imageio/jmagick/pom.xml b/twelvemonkeys-imageio/jmagick/pom.xml new file mode 100755 index 00000000..661d5e34 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + com.twelvemonkeys.imageio + twelvemonkeys-imageio-jmagick + 2.1 + TwelveMonkeys ImageIO JMagick Plugin + + JMagick Home page + for more information.]]> + + + + twelvemonkeys-imageio + com.twelvemonkeys + 2.1 + + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + tests + + + jmagick + jmagick + 6.2.4 + provided + + + + \ No newline at end of file diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReader.java new file mode 100755 index 00000000..ebd367ba --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReader.java @@ -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 Harald Kuhr + * @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); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReaderSpi.java new file mode 100755 index 00000000..bd1c6f8c --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReaderSpi.java @@ -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 Harald Kuhr + * @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'); + } + +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageWriter.java new file mode 100755 index 00000000..518568b9 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageWriter.java @@ -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 Harald Kuhr + * @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; + } + }; + + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageWriterSpi.java new file mode 100755 index 00000000..bf7d2adc --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageWriterSpi.java @@ -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.jmagick; + +import java.io.IOException; + +/** + * BMPImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: BMPImageWriterSpi.java,v 1.0 30.jul.2004 18:32:40 haku Exp $ + */ +public class BMPImageWriterSpi extends JMagickImageWriterSpiSupport { + public BMPImageWriterSpi() { + super( + new String[]{"bmp", "BMP"}, + new String[]{"bmp"}, + new String[]{"image/bmp", "image/x-bmp", "image/x-windows-bmp", "image/x-ms-bmp"}, // http://en.wikipedia.org/wiki/Windows_Bitmap + BMPImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmageick.BMPImageReaderSpi"} + ); + } + + @Override + public BMPImageWriter createWriterImpl(Object pExtension) throws IOException { + return new BMPImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageReader.java new file mode 100755 index 00000000..9b2d6483 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageReader.java @@ -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; + +/** + * GIFImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: GIFImageReader.java,v 1.0 Dec 3, 2007 12:53:44 PM haraldk Exp$ + */ +public class GIFImageReader extends JMagickReader { + protected GIFImageReader(final GIFImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageReaderSpi.java new file mode 100755 index 00000000..5d949d13 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageReaderSpi.java @@ -0,0 +1,65 @@ +/* + * 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; + +/** + * GIFImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: GIFImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class GIFImageReaderSpi extends JMagickImageReaderSpiSupport { + public GIFImageReaderSpi() { + super( + new String[]{"gif", "GIF"}, + new String[]{"gif"}, + new String[]{"image/gif", "image/x-gif"}, + GIFImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.GIFImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {'G', 'I', 'F', '8', '7'}, // GIF + // new byte[] {'G', 'I', 'F', '8', '9'}}, + byte[] magic = new byte[6]; + pSource.readFully(magic); + + return magic[0] == 'G' && magic[1] == 'I' && magic[2] == 'F' && + magic[3] == '8' && (magic[4] == '7' || magic[4] == '9'); + } + + protected GIFImageReader createReaderImpl(final Object pExtension) throws IOException { + return new GIFImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageWriter.java new file mode 100755 index 00000000..3248736b --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageWriter.java @@ -0,0 +1,64 @@ +/* + * 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; + +/** + * GIFImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: GIFImageWriter.java,v 1.0 Dec 3, 2007 1:55:34 PM haraldk Exp$ + */ +public class GIFImageWriter extends JMagickWriter { + protected GIFImageWriter(final GIFImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()) { + + @Override + public boolean canWriteCompressed() { + return true; + } + + @Override + public boolean canWriteProgressive() { + return true; + } + + @Override + public boolean isCompressionLossless() { + return true; + } + }; + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageWriterSpi.java new file mode 100755 index 00000000..e8920de4 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/GIFImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * GIFImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: GIFImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class GIFImageWriterSpi extends JMagickImageWriterSpiSupport { + public GIFImageWriterSpi() { + super( + new String[]{"gif", "GIF"}, + new String[]{"gif"}, + new String[]{"image/gif", "image/x-gif"}, + GIFImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.GIFImageReaderSpi"} + ); + } + + protected GIFImageWriter createWriterImpl(final Object pExtension) throws IOException { + return new GIFImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageReader.java new file mode 100755 index 00000000..974f7507 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageReader.java @@ -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; + +/** + * ICOImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ICOImageReader.java,v 1.0 Dec 3, 2007 2:01:52 PM haraldk Exp$ + */ +public class ICOImageReader extends JMagickReader { + public ICOImageReader(final ICOImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageReaderSpi.java new file mode 100755 index 00000000..a565d34e --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageReaderSpi.java @@ -0,0 +1,65 @@ +/* + * 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; + +/** + * ICOImageReaderSpi class description. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: ICOImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class ICOImageReaderSpi extends JMagickImageReaderSpiSupport { + // TODO: Handle CUR + public ICOImageReaderSpi() { + super( + new String[]{"ico", "ICO"}, + new String[]{"ico"}, + new String[]{"image/x-icon", "image/ico"}, + ICOImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.ICOImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {0x0, 0x0, 0x1, 0x0}}, // ICO (best guess) + // new byte[][] {new byte[] {0x00, 0x00, 0x02, 0x00, + // 0x01, 0x00, 0x20, 0x20}}, // CUR + byte[] magic = new byte[4]; + pSource.readFully(magic); + return magic[0] == 0x0 && magic[1] == 0x0 && magic[2] == 0x1 && magic[3] == 0x0; + } + + protected ICOImageReader createReaderImpl(final Object pExtension) throws IOException { + return new ICOImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageWriter.java new file mode 100755 index 00000000..d26767c6 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageWriter.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * ICOImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ICOImageWriter.java,v 1.0 Dec 3, 2007 2:21:07 PM haraldk Exp$ + */ +public class ICOImageWriter extends JMagickWriter { + public ICOImageWriter(final ICOImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageWriterSpi.java new file mode 100755 index 00000000..64edd452 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/ICOImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * ICOImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: ICOImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class ICOImageWriterSpi extends JMagickImageWriterSpiSupport { + public ICOImageWriterSpi() { + super( + new String[]{"ico", "ICO"}, + new String[]{"ico"}, + new String[]{"image/x-icon", "image/ico"}, + ICOImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.ICOImageReaderSpi"} + ); + } + + protected ICOImageWriter createWriterImpl(final Object pExtension) throws IOException { + return new ICOImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagick.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagick.java new file mode 100755 index 00000000..d04dc0d8 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagick.java @@ -0,0 +1,83 @@ +/* + * 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 com.twelvemonkeys.lang.SystemUtil; +import magick.MagickImage; + +import java.io.IOException; +import java.util.Properties; + +/** + * JMagick helper class, to iron out a few wrinkles. + *

+ * + * @author Harald Kuhr + * @version $Id: JMagick.java,v 1.0 16.jan.2006 14:20:30 haku Exp$ + */ +class JMagick { + + static Properties sProperties; + + static { + // NOTE: Workaround for strange defaults in JMagick: + // Makes JMagick load dll's using the context classloader, rather than + // the system classloader as default... + if (System.getProperty("jmagick.systemclassloader") == null) { + System.setProperty("jmagick.systemclassloader", "no"); + } + + // Makes this class fail on init, if JMagick is unavailable + try { + MagickImage.class.getClass(); + new MagickImage(); // Loads the JNI lib, if needed + } + catch (Error e) { + System.err.print("JMagick not available: "); + System.err.println(e); + System.err.println("Make sure JMagick libraries are available in java.library.path. Current value: "); + System.err.println("\"" + System.getProperty("java.library.path") + "\""); + + throw e; + } + + // Load custom properties for the JMagickReader + try { + sProperties = SystemUtil.loadProperties(JMagickReader.class); + } + catch (IOException e) { + System.err.println("Could not read properties for JMagickReader: " + e.getMessage()); + e.printStackTrace(); + } + } + + static void init() { + // No-op. + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageReaderSpiSupport.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageReaderSpiSupport.java new file mode 100755 index 00000000..528ff26e --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageReaderSpiSupport.java @@ -0,0 +1,148 @@ +/* + * 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 com.twelvemonkeys.lang.SystemUtil; + +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; + +/** + * JMagickImageReaderSpiSupport + *

+ * + * @author Harald Kuhr + * @version $Id: JimiImageReaderSpiSupport.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $ + */ +abstract class JMagickImageReaderSpiSupport extends ImageReaderSpi { + + final static boolean AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.jmagick.JMagick"); + + /** + * Creates a JMagickImageReaderSpiSupport + * + * @param pFormatNames format names + * @param pSuffixes format suffixes + * @param pMimeTypes format MIME types + * @param pReaderClassName format reader class name + * @param pWriterSpiNames format writer service provider namses + */ + protected JMagickImageReaderSpiSupport(final String[] pFormatNames, + final String[] pSuffixes, + final String[] pMimeTypes, + final String pReaderClassName, + final String[] pWriterSpiNames) { + super( + "TwelveMonkeys", // Vendor name + "2.0", // Version + AVAILABLE ? pFormatNames : new String[]{""}, // Names + AVAILABLE ? pSuffixes : null, // Suffixes + AVAILABLE ? pMimeTypes : null, // Mime-types + pReaderClassName, // Reader class name + ImageReaderSpi.STANDARD_INPUT_TYPE, // Output types + pWriterSpiNames, // 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 && AVAILABLE && canDecode0((ImageInputStream) pSource); + } + + private boolean canDecode0(ImageInputStream pSource) throws IOException { + pSource.mark(); + try { + return canDecode(pSource); + } + finally { + pSource.reset(); + } + } + + /** + * Specifies if this provider's reader may decode the provided input. + * Stream mark/reset is handled, and need not be taken care of. + * + * @param pSource the source image input stream + * @return {@code true} if the inout can be decoded + * @throws IOException if an I/O exception occurs during the process + */ + abstract boolean canDecode(ImageInputStream pSource) throws IOException; + + public final ImageReader createReaderInstance(Object pExtension) throws IOException { + try { + return createReaderImpl(pExtension); + } + catch (Throwable t) { + // Wrap in IOException if the reader can't be instantiated. + // This makes the IIORegistry deregister this service provider + IOException exception = new IOException(t.getMessage()); + exception.initCause(t); + throw exception; + } + } + + protected abstract JMagickReader createReaderImpl(Object pExtension) throws IOException; + + public String getDescription(Locale pLocale) { + return "JMagick " + getFormatNames()[0].toUpperCase() + " image reader"; + } + + @Override + @SuppressWarnings({"unchecked"}) + public void onRegistration(ServiceRegistry pRegistry, Class pCategory) { + if (!AVAILABLE) { + pRegistry.deregisterServiceProvider(this, pCategory); + } + } + + /** + * Specifies if the reader created by this provider should use temporary + * file, instead of passing the data in-memory to the native reader. + * + * @return {@code true} if the reader should use a temporary file + */ + public boolean useTempFile() { + return "TRUE".equalsIgnoreCase(JMagick.sProperties.getProperty(getFormatNames()[0] + ".useTempFile")); + } +} + diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageWriterSpiSupport.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageWriterSpiSupport.java new file mode 100755 index 00000000..f738cfd2 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageWriterSpiSupport.java @@ -0,0 +1,122 @@ +/* + * 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.ImageTypeSpecifier; +import javax.imageio.ImageWriter; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.spi.ImageWriterSpi; +import javax.imageio.spi.ServiceRegistry; +import java.io.IOException; +import java.util.Locale; + +/** + * JMagickImageWriterSpiSupport + *

+ * + * @author Harald Kuhr + * @version $Id: JMagickImageWriterSpiSupport.java,v 1.0 17.jan.2006 00:04:32 haku Exp$ + */ +abstract class JMagickImageWriterSpiSupport extends ImageWriterSpi { + + private final static boolean AVAILABLE = JMagickImageReaderSpiSupport.AVAILABLE; + + /** + * Creates a JMagickImageReaderSpiSupport + * + * @param pFormatNames format names + * @param pSuffixes format suffixes + * @param pMimeTypes format MIME types + * @param pWriterClassName format writer class name + * @param pReaderSpiNames format reader service provider namses + */ + protected JMagickImageWriterSpiSupport(final String[] pFormatNames, + final String[] pSuffixes, + final String[] pMimeTypes, + final String pWriterClassName, + final String[] pReaderSpiNames) { + super( + "TwelveMonkeys", // Vendor name + "2.0", // Version + AVAILABLE ? pFormatNames : new String[]{""}, // Names + AVAILABLE ? pSuffixes : null, // Suffixes + AVAILABLE ? pMimeTypes : null, // Mime-types + pWriterClassName, // Writer class name + ImageReaderSpi.STANDARD_INPUT_TYPE, // Output types + pReaderSpiNames, // 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 + ); + } + + /** + * This implementations simply returns {@code true}. + * + * @param pType ignored + * @return {@code true} unless overriden + */ + public boolean canEncodeImage(ImageTypeSpecifier pType) { + return true; + } + + public final ImageWriter createWriterInstance(Object pExtension) throws IOException { + try { + return createWriterImpl(pExtension); + } + 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; + } + } + + protected abstract JMagickWriter createWriterImpl(final Object pExtension) throws IOException; + + public String getDescription(Locale pLocale) { + return "JMagick " + getFormatNames()[0].toUpperCase() + " image writer"; + } + + @Override + @SuppressWarnings({"unchecked"}) + public void onRegistration(ServiceRegistry pRegistry, Class pCategory) { + if (!AVAILABLE) { + pRegistry.deregisterServiceProvider(this, pCategory); + } + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickReader.java new file mode 100755 index 00000000..20c96a58 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickReader.java @@ -0,0 +1,335 @@ +/* + * 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 com.twelvemonkeys.image.ImageUtil; +import com.twelvemonkeys.image.MagickUtil; +import com.twelvemonkeys.image.MonochromeColorModel; +import com.twelvemonkeys.imageio.ImageReaderBase; +import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier; +import com.twelvemonkeys.io.FileUtil; +import magick.ImageInfo; +import magick.ImageType; +import magick.MagickException; +import magick.MagickImage; + +import javax.imageio.IIOException; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.spi.ImageReaderSpi; +import java.awt.*; +import java.awt.color.ColorSpace; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.io.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * JMagickReader, uses JMagick, an open source Java interface of ImageMagick to + * read images. This {@code ImageReader} has certain limitations (see below), + * but the number of formats supported should by far outweigh these + * limitatations. + *

+ * Limitations: JMagick is not stream-based, meaning image (file) data + * must be read into a temporary byte array, potentially causing performance + * penalties. + * ImageMagick itself might even use the file extension to determine file + * format, in such cases a temporary file must be written to disk before + * the image data can be read. While this is perfomed transparently to the user + * there are still performance penalties related to the extra disk I/O. + *

+ * + * Note: This class relies on JMagick, which ues JNI and native code. You need + * to have the JMagick and ImageMagick shared libraries (or DLLs) in Java's + * {@code java.library.path} for this class to work. + * + * + * @see ImageMagick homepage + * @see JMagick homepage + * + * @author Harald Kuhr + * @version $Id: JMagickReader.java,v 1.0 24.jan.2005 10:33:05 haku Exp$ + * + * @todo System property to allow all images be read using temp file (reducing + * memory consumption): "com.twelvemonkeys.imageio.plugins.jmagick.useTempFile" + */ +abstract class JMagickReader extends ImageReaderBase { + + // Make sure the JMagick init is run, or class init will fail + static { + JMagick.init(); + } + + private static final ColorModel CM_GRAY_ALPHA = + new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), true, true, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); + + private final boolean mUseTempFile; + private File mTempFile; + + private MagickImage mImage; + private Dimension mSize; + + protected JMagickReader(final JMagickImageReaderSpiSupport pProvider) { + this(pProvider, pProvider.useTempFile()); + } + + protected JMagickReader(final ImageReaderSpi pProvider, final boolean pUseTemp) { + super(pProvider); + mUseTempFile = pUseTemp; + } + + @Override + protected void resetMembers() { + if (mTempFile != null) { + mTempFile.delete(); + } + mTempFile = null; + + if (mImage != null) { + mImage.destroyImages(); + } + mImage = null; + + mSize = null; + } + + // TODO: Handle multi-image formats + // if (mImage.hasFrames()) { + // int count = mImage.getNumFrames(); + // MagickImage[] images = mImage.breakFrames(); + // } + + public Iterator getImageTypes(int pIndex) throws IOException { + checkBounds(pIndex); + + init(pIndex); + + // TODO: FIX ME! + // - Use factory methods for ImageTypeSpecifier, create factory methods if necessary + List specs = new ArrayList(); + try { + ColorModel cm; + // NOTE: These are all fall-through by intention + switch (mImage.getImageType()) { + case ImageType.BilevelType: + specs.add(IndexedImageTypeSpecifier.createFromIndexColorModel(MonochromeColorModel.getInstance())); + case ImageType.GrayscaleType: +// cm = MagickUtil.CM_GRAY_OPAQUE; +// specs.add(new ImageTypeSpecifier( +// cm, +// cm.createCompatibleSampleModel(1, 1) +// )); + specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY)); + case ImageType.GrayscaleMatteType: + cm = CM_GRAY_ALPHA; + specs.add(new ImageTypeSpecifier( + cm, + cm.createCompatibleSampleModel(1, 1) + )); + case ImageType.PaletteType: + specs.add(IndexedImageTypeSpecifier.createFromIndexColorModel( + MagickUtil.createIndexColorModel(mImage.getColormap(), false) + )); + case ImageType.PaletteMatteType: + specs.add(IndexedImageTypeSpecifier.createFromIndexColorModel( + MagickUtil.createIndexColorModel(mImage.getColormap(), true) + )); + case ImageType.TrueColorType: +// cm = MagickUtil.CM_COLOR_OPAQUE; +// specs.add(new ImageTypeSpecifier( +// cm, +// cm.createCompatibleSampleModel(1, 1) +// )); + specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)); + case ImageType.TrueColorMatteType: +// cm = MagickUtil.CM_COLOR_ALPHA; +// specs.add(new ImageTypeSpecifier( +// cm, +// cm.createCompatibleSampleModel(1, 1) +// )); + specs.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE)); + break; + case ImageType.ColorSeparationType: + case ImageType.ColorSeparationMatteType: + case ImageType.OptimizeType: + default: + throw new MagickException("Unknown JMagick image type: " + mImage.getImageType()); + } + } + catch (MagickException e) { + throw new IIOException(e.getMessage(), e); + } + + return specs.iterator(); + } + + public int getWidth(int pIndex) throws IOException { + checkBounds(pIndex); + + if (mSize == null) { + init(0); + } + return mSize != null ? mSize.width : -1; + } + + public int getHeight(int pIndex) throws IOException { + checkBounds(pIndex); + + if (mSize == null) { + init(0); + } + return mSize != null ? mSize.height : -1; + } + + public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException { + try { + init(pIndex); + + processImageStarted(pIndex); + + // Some more waste of time and space... + Dimension size = mSize; + + if (pParam != null) { + // Source region + // TODO: Maybe have to do some tests, to check if we are within bounds... + Rectangle sourceRegion = pParam.getSourceRegion(); + if (sourceRegion != null) { + mImage = mImage.cropImage(sourceRegion); + size = sourceRegion.getSize(); + } + + // Subsampling + if (pParam.getSourceXSubsampling() > 1 || pParam.getSourceYSubsampling() > 1) { + int w = size.width / pParam.getSourceXSubsampling(); + int h = size.height / pParam.getSourceYSubsampling(); + + mImage = mImage.sampleImage(w, h); + size = new Dimension(w, h); + } + } + + if (abortRequested()) { + processReadAborted(); + return ImageUtil.createClear(size.width, size.height, null); + } + + processImageProgress(10f); + BufferedImage buffered = MagickUtil.toBuffered(mImage); + processImageProgress(100f); + + /**/ + //System.out.println("Created image: " + buffered); + //System.out.println("ColorModel: " + buffered.getColorModel().getClass().getName()); + //if (buffered.getColorModel() instanceof java.awt.image.IndexColorModel) { + // java.awt.image.IndexColorModel cm = (java.awt.image.IndexColorModel) buffered.getColorModel(); + // for (int i = 0; i < cm.getMapSize(); i++) { + // System.out.println("0x" + Integer.toHexString(cm.getRGB(i))); + // } + //} + //*/ + + /** + System.out.println("Colorspace: " + mImage.getColorspace()); + System.out.println("Depth: " + mImage.getDepth()); + System.out.println("Format: " + mImage.getImageFormat()); + System.out.println("Type: " + mImage.getImageType()); + System.out.println("IPTCProfile: " + StringUtil.deepToString(mImage.getIptcProfile())); + System.out.println("StorageClass: " + mImage.getStorageClass()); + //*/ + + processImageComplete(); + + return buffered; + } + catch (MagickException e) { + // Wrap in IIOException + throw new IIOException(e.getMessage(), e); + } + } + + private synchronized void init(int pIndex) throws IOException { + checkBounds(pIndex); + + try { + if (mImage == null) { + // TODO: If ImageInputStream is already file-backed, maybe we can peek into that file? + // At the moment, the cache/file is not accessible, but we could create our own + // FileImageInputStream provider that gives us this access. + if (!mUseTempFile && mImageInput.length() >= 0 && mImageInput.length() <= Integer.MAX_VALUE) { + // This works for most file formats, as long as ImageMagick + // uses the file magic to decide file format + byte[] bytes = new byte[(int) mImageInput.length()]; + mImageInput.readFully(bytes); + + // Unfortunately, this is a waste of space & time... + ImageInfo info = new ImageInfo(); + mImage = new MagickImage(info); + mImage.blobToImage(info, bytes); + } + else { + // Quirks mode: Use temp file to get correct file extension + // (which is even more waste of space & time, but might save memory) + String ext = getFormatName().toLowerCase(); + + mTempFile = File.createTempFile("jmagickreader", "." + ext); + mTempFile.deleteOnExit(); + OutputStream out = new BufferedOutputStream(new FileOutputStream(mTempFile)); + try { + byte[] buffer = new byte[FileUtil.BUF_SIZE]; + int count; + + while ((count = mImageInput.read(buffer)) != -1) { + out.write(buffer, 0, count); + } + + // Flush out stream, to write any remaining buffered data + out.flush(); + } + finally { + out.close(); + } + + ImageInfo info = new ImageInfo(mTempFile.getAbsolutePath()); + mImage = new MagickImage(info); + } + + mSize = mImage.getDimension(); + } + } + catch (MagickException e) { + // Wrap in IIOException + throw new IIOException(e.getMessage(), e); + } + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickWriter.java new file mode 100755 index 00000000..8106bbee --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickWriter.java @@ -0,0 +1,143 @@ +/* + * 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 com.twelvemonkeys.image.ImageUtil; +import com.twelvemonkeys.image.MagickUtil; +import com.twelvemonkeys.imageio.ImageWriterBase; +import magick.ImageInfo; +import magick.MagickException; +import magick.MagickImage; + +import javax.imageio.IIOException; +import javax.imageio.IIOImage; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.metadata.IIOMetadata; +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * JMagickWriter + *

+ * NOTE: This ImageWriter is probably a waste of time and space, as + * all images are converted from the given Rendered/BufferedImage, + * first to 16bit raw ARGB samples, and then to the requested output format. + * This is due to a limitation in the current JMagick API. + * + * @author Harald Kuhr + * @version $Id: JMagickWriter.java,v 1.0 16.jan.2006 13:34:46 haku Exp$ + */ +abstract class JMagickWriter extends ImageWriterBase { + + static { + // Make sure the JMagick init is run... + JMagick.init(); + } + + protected JMagickWriter(final JMagickImageWriterSpiSupport pProvider) { + super(pProvider); + } + + public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) { + throw new UnsupportedOperationException("Method convertImageMetadata not implemented");// TODO: Implement + } + + public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) { + throw new UnsupportedOperationException("Method getDefaultImageMetadata 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); + MagickImage image = null; + try { + // AOI & subsampling + BufferedImage buffered = fakeAOI(ImageUtil.toBuffered(pImage.getRenderedImage()), pParam); + buffered = ImageUtil.toBuffered(fakeSubsampling(buffered, pParam)); + + // Convert to MagickImage + image = MagickUtil.toMagick(buffered); + processImageProgress(33f); + + // Get bytes blob from MagickImage + String format = getFormatName().toLowerCase(); + image.setImageFormat(format); + ImageInfo info = new ImageInfo(); + if (pParam != null && pParam.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT) { + int quality = (int) (pParam.getCompressionQuality() * 100); + //System.out.println("pParam.getCompressionQuality() = " + pParam.getCompressionQuality()); + //System.out.println("quality = " + quality); + //info.setCompression(CompressionType.JPEGCompression); + //image.setCompression(CompressionType.JPEGCompression); + + // TODO: Is quality really correct in all cases? + // TODO: This does not seem to do the trick.. + info.setQuality(quality); + } + byte[] bytes = image.imageToBlob(info); + if (bytes == null) { + throw new IIOException("Could not write image data in " + format + " format."); + } + processImageProgress(67); + + // Write blob to output + mImageOutput.write(bytes); + mImageOutput.flush(); + processImageProgress(100); + } + catch (MagickException e) { + throw new IIOException(e.getMessage(), e); + } + finally { + if (image != null) { + image.destroyImages();// Dispose native memory + } + } + + processImageComplete(); + } + + public ImageWriteParam getDefaultWriteParam() { + return createDefaultWriteParam(); + } + + protected abstract ImageWriteParam createDefaultWriteParam(); + + @Override + public void dispose() { + // Clean up! + super.dispose(); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageReader.java new file mode 100755 index 00000000..cbf3bd4d --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageReader.java @@ -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; + +/** + * JPEG2KImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: JPEG2KImageReader.java,v 1.0 Dec 3, 2007 2:56:30 PM haraldk Exp$ + */ +public class JPEG2KImageReader extends JMagickReader { + public JPEG2KImageReader(final JPEG2KImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageReaderSpi.java new file mode 100755 index 00000000..789b34fb --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageReaderSpi.java @@ -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.jmagick; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * JPEG2KImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: JPEG2KImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class JPEG2KImageReaderSpi extends JMagickImageReaderSpiSupport { + public JPEG2KImageReaderSpi() { + super( + new String[]{"jpeg2000", "jpeg 2000"}, + new String[]{"jp2"}, + new String[]{"image/jp2", "image/jpeg2000"}, + JPEG2KImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.JPEG2KImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {0x00, 0x00, 0x00, 0x0C, 'j', 'P', ' ', ' ', 0x0D, 0x0A, (byte) 0x87, 0x0A}, // JPEG 2000 JP2 format + // new byte[] {(byte) 0xff, 0x4f}}, // JPEG 2000 codestream format + byte[] magic = new byte[12]; + pSource.readFully(magic); + + return (magic[0] == 0x00 && magic[1] == 0x00 && magic[2] == 0x00 && + magic[3] == 0x0C && magic[4] == 'j' && magic[5] == 'P' && + magic[6] == ' ' && magic[7] == ' ' && magic[8] == 0x0D && + magic[9] == 0x0A && magic[10] == (byte) 0x87 && magic[11] == 0x0A) || + (magic[0] == (byte) 0xFF && magic[1] == 0x4F); + } + + protected JPEG2KImageReader createReaderImpl(final Object pExtension) throws IOException { + return new JPEG2KImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageWriter.java new file mode 100755 index 00000000..77a0be7f --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageWriter.java @@ -0,0 +1,65 @@ +/* + * 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; + +/** + * JPEG2KImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: JPEG2KImageWriter.java,v 1.0 Dec 3, 2007 6:22:46 PM haraldk Exp$ + */ +public class JPEG2KImageWriter extends JMagickWriter { + public JPEG2KImageWriter(final JPEG2KImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()) { + + @Override + public boolean canWriteCompressed() { + return true; + } + + @Override + public boolean canWriteProgressive() { + return true; + } + + @Override + public boolean isCompressionLossless() { + // TODO: This depends on the algorithm, JPEG 2000 supports lossless... + return false; + } + }; + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageWriterSpi.java new file mode 100755 index 00000000..0659a035 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEG2KImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * JPEG2KImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: JPEG2KImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class JPEG2KImageWriterSpi extends JMagickImageWriterSpiSupport { + public JPEG2KImageWriterSpi() { + super( + new String[]{"jpeg2000", "jpeg 2000"}, + new String[]{"jp2"}, + new String[]{"image/jp2", "image/jpeg2000"}, + JPEG2KImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.JPEG2KImageReaderSpi"} + ); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new JPEG2KImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageReader.java new file mode 100755 index 00000000..3e488c26 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageReader.java @@ -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; + +/** + * JPEGImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: JPEGImageReader.java,v 1.0 Dec 3, 2007 6:28:10 PM haraldk Exp$ + */ +public class JPEGImageReader extends JMagickReader { + public JPEGImageReader(final JPEGImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageReaderSpi.java new file mode 100755 index 00000000..797714ca --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageReaderSpi.java @@ -0,0 +1,67 @@ +/* + * 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; + +/** + * JPEGImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: JPEGImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class JPEGImageReaderSpi extends JMagickImageReaderSpiSupport { + public JPEGImageReaderSpi() { + super( + new String[]{"jpeg", "JPEG", "exif", "EXIF", "jfif", "JFIF", "jpg", "JPG"}, + new String[]{"jpg", "jpeg", "jpe"}, + new String[]{"image/jpeg", "image/jfif", "image/x-exif"}, + JPEGImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.JPEGImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff, (byte) 0xe0}, + // new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff, (byte) 0xe1}}, // JPEG + // new byte[][] {new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff, (byte) 0xee}}, // JPG + byte[] magic = new byte[4]; + pSource.readFully(magic); + + return magic[0] == (byte) 0xFF && magic[1] == (byte) 0xD8 && magic[2] == (byte) 0xFF && + (magic[3] == (byte) 0xE0 || magic[0] == (byte) 0xE1 || magic[0] == (byte) 0xEE); + + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new JPEGImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageWriter.java new file mode 100755 index 00000000..cdd0f08d --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageWriter.java @@ -0,0 +1,64 @@ +/* + * 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; + +/** + * JPEGImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: JPEGImageWriter.java,v 1.0 Dec 3, 2007 6:40:10 PM haraldk Exp$ + */ +public class JPEGImageWriter extends JMagickWriter { + public JPEGImageWriter(final JPEGImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()) { + + @Override + public boolean canWriteCompressed() { + return true; + } + + @Override + public boolean canWriteProgressive() { + return true; + } + + @Override + public boolean isCompressionLossless() { + return false; + } + }; + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageWriterSpi.java new file mode 100755 index 00000000..a1a11f41 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/JPEGImageWriterSpi.java @@ -0,0 +1,53 @@ +/* + * 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 java.io.IOException; + +/** + * BMPImageReaderSpi class description. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: BMPImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class JPEGImageWriterSpi extends JMagickImageWriterSpiSupport { + public JPEGImageWriterSpi() { + super( + new String[]{"jpeg", "JPEG", "exif", "EXIF", "jfif", "JFIF", "jpg", "JPG"}, + new String[]{"jpg", "jpeg"}, + new String[]{"image/jpeg", "image/jfif", "image/x-exif"}, + JPEGImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.JPEGImageReaderSpi"}); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new JPEGImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCDImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCDImageReader.java new file mode 100755 index 00000000..cfdbf9b2 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCDImageReader.java @@ -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; + +/** + * PCDImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PCDImageReader.java,v 1.0 Dec 3, 2007 6:53:05 PM haraldk Exp$ + */ +public class PCDImageReader extends JMagickReader { + public PCDImageReader(final PCDImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCDImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCDImageReaderSpi.java new file mode 100755 index 00000000..d8851969 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCDImageReaderSpi.java @@ -0,0 +1,71 @@ +/* + * 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; + +/** + * PCDImageReaderSpi + * + * @author Harald Kuhr + * @version $Id: PCDImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $ + */ +public class PCDImageReaderSpi extends JMagickImageReaderSpiSupport { + public PCDImageReaderSpi() { + super( + new String[]{"pcd", "PCD"}, + new String[]{"pcd", "PCD"}, + new String[]{"image/pcd", "image/x-pcd"}, + PCDImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.PCXImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + //final static byte[] PCD_MAGIC = new byte[] {0x50, 0x43, 0x44, 0x5f, 0x49, 0x50, 0x49}; + if (pSource.length() > 2055) { + pSource.seek(2048); + + byte[] magic = new byte[7]; + pSource.readFully(magic); + + // Kodak PhotoCD PCD_IPI + return magic[0] == 'P' && magic[1] == 'C' && magic[2] == 'D' + && magic[3] == '_' && magic[4] == 'I' && magic[5] == 'P' + && magic[6] == 'I'; + } + + return false; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new PCDImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageReader.java new file mode 100755 index 00000000..4c8d1b27 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageReader.java @@ -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; + +/** + * PCXImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PCXImageReader.java,v 1.0 Dec 3, 2007 7:06:06 PM haraldk Exp$ + */ +public class PCXImageReader extends JMagickReader { + public PCXImageReader(final PCXImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageReaderSpi.java new file mode 100755 index 00000000..29f72e75 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageReaderSpi.java @@ -0,0 +1,64 @@ +/* + * 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; + +/** + * PCXImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: PCXImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class PCXImageReaderSpi extends JMagickImageReaderSpiSupport { + public PCXImageReaderSpi() { + super( + new String[]{"pcx", "PCX"}, + new String[]{"pcx"}, + new String[]{"image/pcx", "image/x-pcx"}, + PCXImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.PCXImageReaderSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {0x0a, -1, 0x01}}, // PCX + byte[] magic = new byte[4]; + pSource.readFully(magic); + return magic[0] == 0x0A && + (magic[1] == 0x02 || magic[1] == 0x03 || magic[1] == 0x05) && + magic[2] == 0x01 && magic[3] == 0x01; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new PCXImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageWriter.java new file mode 100755 index 00000000..2154d886 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageWriter.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * PCXImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PCXImageWriter.java,v 1.0 Dec 3, 2007 7:12:58 PM haraldk Exp$ + */ +public class PCXImageWriter extends JMagickWriter { + public PCXImageWriter(final PCXImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageWriterSpi.java new file mode 100755 index 00000000..07351e16 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PCXImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * PCXImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: PCXImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class PCXImageWriterSpi extends JMagickImageWriterSpiSupport { + public PCXImageWriterSpi() { + super( + new String[]{"pcx", "PCX"}, + new String[]{"pcx"}, + new String[]{"image/pcx", "image/x-pcx"}, + PCXImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.PCXImageReaderSpi"} + ); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new PCXImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PDBImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PDBImageReader.java new file mode 100755 index 00000000..ea160780 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PDBImageReader.java @@ -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; + +/** + * PDBImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PDBImageReader.java,v 1.0 Dec 3, 2007 7:14:23 PM haraldk Exp$ + */ +public class PDBImageReader extends JMagickReader { + public PDBImageReader(final PDBImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PDBImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PDBImageReaderSpi.java new file mode 100755 index 00000000..be7dad9f --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PDBImageReaderSpi.java @@ -0,0 +1,66 @@ +/* + * 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; + +/** + * PDBImageReaderSpi + * + * @author Harald Kuhr + * @version $Id: PDBImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $ + */ +public class PDBImageReaderSpi extends JMagickImageReaderSpiSupport { + public PDBImageReaderSpi() { + super( + new String[]{"pdb", "PDB"}, + new String[]{"pdb", "PDB"}, + new String[]{"image/x-palm-db", "image/palm-db"}, + PDBImageReader.class.getName(), + null + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + //'v', 'I', 'M', 'G', 'V', 'i', 'e', 'w' + // [11 byte offset] + //00 00 00 00 00 00 00 00 + //00 00 00 00 00 00 00 00 + //00 00 00 00 00 00 00 00 [11 byte offset] + //........ + //........ + //........ + return false; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new PDBImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageReader.java new file mode 100755 index 00000000..ae2c7c98 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageReader.java @@ -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; + +/** + * PNGImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PNGImageReader.java,v 1.0 Dec 3, 2007 8:18:28 PM haraldk Exp$ + */ +public class PNGImageReader extends JMagickReader { + public PNGImageReader(final PNGImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageReaderSpi.java new file mode 100755 index 00000000..091b6c4a --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageReaderSpi.java @@ -0,0 +1,67 @@ +/* + * 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; + +/** + * PNGImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: PNGImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class PNGImageReaderSpi extends JMagickImageReaderSpiSupport { + public PNGImageReaderSpi() { + super( + new String[]{"png", "PNG"}, + new String[]{"png"}, + new String[]{"image/png", "image/x-png"}, + PNGImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.PNGImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {(byte) 0x89, (byte) 'P', (byte) 'N', (byte) 'G', // PNG + // 0x0d, 0x0a, 0x1a, 0x0a,}}, + byte[] magic = new byte[8]; + pSource.readFully(magic); + return magic[0] == (byte) 0x89 && + magic[1] == 'P' && magic[2] == 'N' && magic[3] == 'G' && + magic[4] == 0x0d && magic[5] == 0x0a && + magic[6] == 0x1a && magic[7] == 0x0a; + + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new PNGImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageWriter.java new file mode 100755 index 00000000..db5bec6d --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageWriter.java @@ -0,0 +1,64 @@ +/* + * 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; + +/** + * PNGImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PNGImageWriter.java,v 1.0 Dec 3, 2007 8:25:14 PM haraldk Exp$ + */ +public class PNGImageWriter extends JMagickWriter { + public PNGImageWriter(final PNGImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()) { + + @Override + public boolean canWriteCompressed() { + return true; + } + + @Override + public boolean canWriteProgressive() { + return true; + } + + @Override + public boolean isCompressionLossless() { + return true; + } + }; + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageWriterSpi.java new file mode 100755 index 00000000..2a10c70c --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNGImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * PNGImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: PNGImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class PNGImageWriterSpi extends JMagickImageWriterSpiSupport { + public PNGImageWriterSpi() { + super( + new String[]{"png", "PNG"}, + new String[]{"png"}, + new String[]{"image/png", "image/x-png"}, + PNGImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.PNGImageReaderSpi"} + ); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new PNGImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNMImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNMImageReader.java new file mode 100755 index 00000000..fb5ddd16 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNMImageReader.java @@ -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; + +/** + * PNMImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PNMImageReader.java,v 1.0 Dec 3, 2007 8:27:31 PM haraldk Exp$ + */ +public class PNMImageReader extends JMagickReader { + public PNMImageReader(final PNMImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNMImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNMImageReaderSpi.java new file mode 100755 index 00000000..9339bef0 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PNMImageReaderSpi.java @@ -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.plugins.jmagick; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * PNMImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: PNMImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class PNMImageReaderSpi extends JMagickImageReaderSpiSupport { + public PNMImageReaderSpi() { + super( + new String[]{ + "pnm", "PNM", "pbm", "PBM", "pgm", "PGM", "ppm", "PPM" + }, + new String[]{ + "pbm", "PBM", "pgm", "PGM", "ppm", "PPM" + }, + new String[]{ + "image/portable-pixmap", "image/x-portable-pixmap", + "image/portable-bitmap", "image/x-portable-bitmap", + "image/portable-graymap", "image/x-portable-graymap", "image/x-portable-graymap", + "image/portable-anymap", "image/x-portable-anymap", "application/x-portable-anymap" + }, + PNMImageReader.class.getName(), + null + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {'P', '1'}, new byte[] {'P', '4'}}, // PBM ascii & raw + // new byte[][] {new byte[] {'P', '2'}, new byte[] {'P', '5'}}, // PGM ascii & raw + // new byte[][] {new byte[] {'P', '3'}, new byte[] {'P', '6'}}, // PPM ascii & raw + byte[] magic = new byte[2]; + pSource.readFully(magic); + return magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6'; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new PNMImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PSDImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PSDImageReader.java new file mode 100755 index 00000000..419a3966 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PSDImageReader.java @@ -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; + +/** + * PSDImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDImageReader.java,v 1.0 Dec 3, 2007 8:33:19 PM haraldk Exp$ + */ +public class PSDImageReader extends JMagickReader { + public PSDImageReader(final PSDImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PSDImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PSDImageReaderSpi.java new file mode 100755 index 00000000..4b6e53a6 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/PSDImageReaderSpi.java @@ -0,0 +1,62 @@ +/* + * 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; + +/** + * PSDImageReaderSpi class description. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: PSDImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class PSDImageReaderSpi extends JMagickImageReaderSpiSupport { + public PSDImageReaderSpi() { + super( + new String[]{"psd", "PSD"}, + new String[]{"psd"}, + new String[]{"image/psd", "image/x-psd", "application/x-photoshop"}, + PSDImageReader.class.getName(), + null + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {'8', 'B', 'P', 'S'}}, // PSD + byte[] magic = new byte[4]; + pSource.readFully(magic); + return magic[0] == '8' && magic[1] == 'B' && magic[2] == 'P' && magic[3] == 'S'; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new PSDImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/SWFImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/SWFImageReader.java new file mode 100755 index 00000000..0606056f --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/SWFImageReader.java @@ -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; + +/** + * SWFImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: SWFImageReader.java,v 1.0 Dec 3, 2007 8:36:59 PM haraldk Exp$ + */ +public class SWFImageReader extends JMagickReader { + public SWFImageReader(final SWFImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/SWFImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/SWFImageReaderSpi.java new file mode 100755 index 00000000..77ff47d9 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/SWFImageReaderSpi.java @@ -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.jmagick; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * SWFImageReaderSpi class description. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: SWFImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class SWFImageReaderSpi extends JMagickImageReaderSpiSupport { + public SWFImageReaderSpi() { + super( + new String[]{"flash", "FLASH"}, + new String[]{"swf"}, + new String[]{"application/x-shockwave-flash"}, + SWFImageReader.class.getName(), + null + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + byte[] magic = new byte[3]; + pSource.readFully(magic); + + // TODO: FLV? + return (magic[0] == 'C' || magic[0] == 'F') && magic[1] == 'W' && magic[2] == 'S'; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new SWFImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageReader.java new file mode 100755 index 00000000..d348cc07 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageReader.java @@ -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; + +/** + * TIFFImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: TIFFImageReader.java,v 1.0 Dec 3, 2007 11:33:48 PM haraldk Exp$ + */ +public class TIFFImageReader extends JMagickReader { + public TIFFImageReader(final TIFFImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageReaderSpi.java new file mode 100755 index 00000000..2c727382 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageReaderSpi.java @@ -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.jmagick; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * TargaImageReaderSpi + * + * @author Harald Kuhr + * @version $Id: TargaImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $ + */ +public class TIFFImageReaderSpi extends JMagickImageReaderSpiSupport { + public TIFFImageReaderSpi() { + super( + new String[]{"tiff", "TIFF"}, + new String[]{"tif", "tiff"}, + new String[]{"image/x-tiff", "image/tiff"}, + TIFFImageReader.class.getName(), + new String[] {"com.twlevemonkeys.imageio.plugins.jmagick.TIFFImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {'M', 'M', 0, 42}, // TIFF Motorola byte order + // new byte[] {'I', 'I', 42, 0}}, // TIFF Intel byte order + byte[] magic = new byte[4]; + pSource.readFully(magic); + return (magic[0] == 'M' && magic[1] == 'M' && magic[2] == 0 && magic[3] == 42) || + (magic[0] == 'I' && magic[1] == 'I' && magic[2] == 42 && magic[3] == 0); + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new TIFFImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageWriter.java new file mode 100755 index 00000000..0eeef7fa --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageWriter.java @@ -0,0 +1,53 @@ +/* + * 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; + +/** + * TIFFImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: TIFFImageWriter.java,v 1.0 Dec 3, 2007 11:35:58 PM haraldk Exp$ + */ +public class TIFFImageWriter extends JMagickWriter { + public TIFFImageWriter(final TIFFImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()) { + @Override + public boolean canWriteCompressed() { + return true; + } + }; + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageWriterSpi.java new file mode 100755 index 00000000..7812a38c --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TIFFImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * TargaImageWriterSpi class description. + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: TargaImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class TIFFImageWriterSpi extends JMagickImageWriterSpiSupport { + public TIFFImageWriterSpi() { + super( + new String[]{"tiff", "TIFF"}, + new String[]{"tif", "tiff"}, + new String[]{"image/x-tiff", "image/tiff"}, + TIFFImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.TIFFImageReaderSpi"} + ); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new TIFFImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageReader.java new file mode 100755 index 00000000..40465ad3 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageReader.java @@ -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; + +/** + * TargaImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: TargaImageReader.java,v 1.0 Dec 3, 2007 10:23:06 PM haraldk Exp$ + */ +public class TargaImageReader extends JMagickReader { + public TargaImageReader(final TargaImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageReaderSpi.java new file mode 100755 index 00000000..6d838b1c --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageReaderSpi.java @@ -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.jmagick; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * TargaImageReaderSpi + * + * @author Harald Kuhr + * @version $Id: TargaImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $ + */ +public class TargaImageReaderSpi extends JMagickImageReaderSpiSupport { + public TargaImageReaderSpi() { + super( + new String[]{"tga", "targa", "TGA", "TARGA"}, + new String[]{"tga"}, + new String[]{"image/x-tga", "image/targa"}, + TargaImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.TargaImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // // TODO: Targa 1989 signature look like (bytes 8-23 of 26 LAST bytes): + // // 'T', 'R', 'U', 'E', 'V', 'I', 'S', 'I', 'O', 'N', '-', 'X', 'F', 'I', 'L', 'E' + // // Targa 1987: + // new byte[][] {new byte[] {-1, 0x01, 0x01}, // Type 1: CM + // /* + // TODO: Figure out how to not interfere with CUR: 0x00000200 + // new byte[] {-1, 0x00, 0x02},*/ new byte[] {-1, 0x01, 0x02}, // T2: RGB w & w/o CM + // new byte[] {-1, 0x00, 0x03}, // Type 3: B/W + // new byte[] {-1, 0x01, 0x09}, // Type 9: RLE CM + // new byte[] {-1, 0x00, 0x0a}, new byte[] {-1, 0x01, 0x0a}, // T10: RLE RGB w & w/o CM + // new byte[] {-1, 0x00, 0x0b}, // Type 11: Compressed B/W + // new byte[] {-1, 0x01, 0x20}, // Type 31: Compressed CM + // new byte[] {-1, 0x01, 0x21}, // Type 32: Compressed CM, 4 pass + // }, + pSource.seek(pSource.length() - 18); + byte[] magic = new byte[18]; + pSource.readFully(magic); + + return "TRUEVISIOM-XFILE".equals(new String(magic, 0, 16)); + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new TargaImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageWriter.java new file mode 100755 index 00000000..e58819b3 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageWriter.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * TargaImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: TargaImageWriter.java,v 1.0 Dec 3, 2007 11:29:43 PM haraldk Exp$ + */ +public class TargaImageWriter extends JMagickWriter { + public TargaImageWriter(final TargaImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageWriterSpi.java new file mode 100755 index 00000000..a78f2e3a --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/TargaImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * TargaImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: TargaImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class TargaImageWriterSpi extends JMagickImageWriterSpiSupport { + public TargaImageWriterSpi() { + super( + new String[]{"tga", "targa", "TGA", "TARGA"}, + new String[]{"tga"}, + new String[]{"image/targa", "image/x-tga"}, + TargaImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.TargaImageReaderSpi"} + ); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new TargaImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageReader.java new file mode 100755 index 00000000..f41f55e8 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageReader.java @@ -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; + +/** + * WBMPImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: WBMPImageReader.java,v 1.0 Dec 3, 2007 11:44:51 PM haraldk Exp$ + */ +public class WBMPImageReader extends JMagickReader { + public WBMPImageReader(final WBMPImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageReaderSpi.java new file mode 100755 index 00000000..ecb2510f --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageReaderSpi.java @@ -0,0 +1,83 @@ +/* + * 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 java.io.IOException; +import javax.imageio.stream.ImageInputStream; + +/** + * WBMPImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: WBMPImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class WBMPImageReaderSpi extends JMagickImageReaderSpiSupport { + public WBMPImageReaderSpi() { + super( + new String[]{"wbmp", "WBMP"}, + new String[]{"wbmp"}, + new String[]{"image/vnd.wap.wbmp"}, + WBMPImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.WBMPImageWriterSpi"}); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {0, 0}}, // WBMP + byte[] magic = new byte[2]; + pSource.readFully(magic); + return magic[0] == 0x00 && magic[1] == 0x00 && + readMultiByteInteger(pSource) > 0 && readMultiByteInteger(pSource) > 0;// Positive size + // TODO: Consider testin if the size of the stream after the header matches + // the dimensions: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6331418 + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new WBMPImageReader(this); + } + + private static int readMultiByteInteger(ImageInputStream pStream) throws IOException { + int value = 0; + int b; + + // Read while continuation bit is set + while ((b = pStream.read()) >= 0) { + value = (value << 7) + (b & 0x7f); + + // Test continuation bit, if not set, return value + if ((b & 0x80) == 0) { + return value; + } + } + + // If we got here, value could not be read + return -1; + } + +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageWriter.java new file mode 100755 index 00000000..b22d33b2 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageWriter.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * WBMPImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: WBMPImageWriter.java,v 1.0 Dec 4, 2007 12:04:50 AM haraldk Exp$ + */ +public class WBMPImageWriter extends JMagickWriter { + public WBMPImageWriter(final WBMPImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageWriterSpi.java new file mode 100755 index 00000000..2a549ce7 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WBMPImageWriterSpi.java @@ -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.jmagick; + +import java.io.IOException; + +/** + * WBMPImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: WBMPImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class WBMPImageWriterSpi extends JMagickImageWriterSpiSupport { + public WBMPImageWriterSpi() { + super( + new String[]{"wbmp", "WBMP"}, + new String[]{"wbmp"}, + new String[]{"image/vnd.wap.wbmp"}, + WBMPImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.WBMPImageReaderSpi"} + ); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new WBMPImageWriter(this); + } +} + diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WMFImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WMFImageReader.java new file mode 100755 index 00000000..3ec30e53 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WMFImageReader.java @@ -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; + +/** + * WMFImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: WMFImageReader.java,v 1.0 Dec 4, 2007 12:07:53 AM haraldk Exp$ + */ +public class WMFImageReader extends JMagickReader { + public WMFImageReader(final WMFImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WMFImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WMFImageReaderSpi.java new file mode 100755 index 00000000..88f1cd9f --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/WMFImageReaderSpi.java @@ -0,0 +1,65 @@ +/* + * 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; + +/** + * WMFImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: WMFImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class WMFImageReaderSpi extends JMagickImageReaderSpiSupport { + public WMFImageReaderSpi() { + super( + new String[]{"wmf", "WMF"}, + new String[]{"wmf"}, + new String[]{"image/x-windows-metafile"}, + WMFImageReader.class.getName(), + null + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {(byte) 0xd7, (byte) 0xcd, (byte) 0xc6, // WMF + // (byte) 0x9a, (byte) 0x00, (byte) 0x00,}}, + byte[] magic = new byte[6]; + pSource.readFully(magic); + return magic[0] == (byte) 0xD7 && magic[2] == (byte) 0xCD && + magic[2] == (byte) 0xC6 && magic[3] == (byte) 0x9A && + magic[4] == (byte) 0x00 && magic[5] == (byte) 0x00; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new WMFImageReader(this); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageReader.java new file mode 100755 index 00000000..6d97442a --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageReader.java @@ -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; + +/** + * XBMImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: XBMImageReader.java,v 1.0 Dec 4, 2007 12:14:03 AM haraldk Exp$ + */ +public class XBMImageReader extends JMagickReader { + public XBMImageReader(final XBMImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageReaderSpi.java new file mode 100755 index 00000000..de6c7ec9 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageReaderSpi.java @@ -0,0 +1,62 @@ +/* + * 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; + +/** + * XBMImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: XBMImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class XBMImageReaderSpi extends JMagickImageReaderSpiSupport { + public XBMImageReaderSpi() { + super( + new String[]{"xbm", "XBM"}, + new String[]{"xbm"}, + new String[]{"image/xbm", "image/x-xbm"}, + XBMImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.XBMImageWriterSpi"}); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {'/', '*', ' ', 'X', 'B', 'M'}}, // X BitMap + byte[] magic = new byte[6]; + pSource.readFully(magic); + return magic[0] == '/' && magic[1] == '*' && magic[2] == ' ' && + magic[3] == 'X' && magic[4] == 'B' && magic[5] == 'M'; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new XBMImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageWriter.java new file mode 100755 index 00000000..013447d1 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageWriter.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * XBMImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: XBMImageWriter.java,v 1.0 Dec 4, 2007 12:20:37 AM haraldk Exp$ + */ +public class XBMImageWriter extends JMagickWriter { + public XBMImageWriter(final XBMImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageWriterSpi.java new file mode 100755 index 00000000..3220c973 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XBMImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * XBMImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: XBMImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class XBMImageWriterSpi extends JMagickImageWriterSpiSupport { + public XBMImageWriterSpi() { + super( + new String[]{"xbm", "XBM"}, + new String[]{"xbm"}, + new String[]{"image/xbm", "image/x-xbm"}, + XBMImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.XBMImageReaderSpi"} + ); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new XBMImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageReader.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageReader.java new file mode 100755 index 00000000..e7659f7d --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageReader.java @@ -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; + +/** + * XPMImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: XPMImageReader.java,v 1.0 Dec 4, 2007 12:22:20 AM haraldk Exp$ + */ +public class XPMImageReader extends JMagickReader { + public XPMImageReader(final XPMImageReaderSpi pProvider) { + super(pProvider); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageReaderSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageReaderSpi.java new file mode 100755 index 00000000..76c8618c --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageReaderSpi.java @@ -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.jmagick; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * XPMImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: XPMImageReaderSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class XPMImageReaderSpi extends JMagickImageReaderSpiSupport { + public XPMImageReaderSpi() { + super( + new String[]{"xpm", "XPM"}, + new String[]{"xpm"}, + new String[]{"image/xpm", "image/x-xpm"}, + XPMImageReader.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.XPMImageWriterSpi"} + ); + } + + boolean canDecode(ImageInputStream pSource) throws IOException { + // new byte[][] {new byte[] {'/', '*', ' ', 'X', 'P', 'M'}}, // X PixelMap + byte[] magic = new byte[6]; + pSource.readFully(magic); + return magic[0] == '/' && magic[1] == '*' && magic[2] == ' ' && + magic[3] == 'X' && magic[4] == 'P' && magic[5] == 'M'; + } + + protected JMagickReader createReaderImpl(final Object pExtension) throws IOException { + return new XPMImageReader(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageWriter.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageWriter.java new file mode 100755 index 00000000..94f3b7c6 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageWriter.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * XPMImageWriter + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: XPMImageWriter.java,v 1.0 Dec 4, 2007 12:26:46 AM haraldk Exp$ + */ +public class XPMImageWriter extends JMagickWriter { + public XPMImageWriter(final XPMImageWriterSpi pProvider) { + super(pProvider); + } + + protected ImageWriteParam createDefaultWriteParam() { + return new ImageWriteParam(getLocale()); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageWriterSpi.java b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageWriterSpi.java new file mode 100755 index 00000000..d4006441 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/XPMImageWriterSpi.java @@ -0,0 +1,54 @@ +/* + * 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 java.io.IOException; + +/** + * XPMImageWriterSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haku $ + * @version $Id: XPMImageWriterSpi.java,v 1.0 30.jul.2004 20:39:48 haku Exp $ + */ +public class XPMImageWriterSpi extends JMagickImageWriterSpiSupport { + public XPMImageWriterSpi() { + super( + new String[]{"xpm", "XPM"}, + new String[]{"xpm"}, + new String[]{"image/xpm", "image/x-xpm"}, + XPMImageWriter.class.getName(), + new String[]{"com.twelvemonkeys.imageio.plugins.jmagick.XPMImageReaderSpi"} + ); + } + + protected JMagickWriter createWriterImpl(final Object pExtension) throws IOException { + return new XPMImageWriter(this); + } +} diff --git a/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/package.html b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/package.html new file mode 100755 index 00000000..2eea895a --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/java/com/twelvemonkeys/imageio/plugins/jmagick/package.html @@ -0,0 +1,13 @@ +JMagick plugin for ImageIO that uses JMagick, an open source Java interface for +ImageMagick to read and write images. +

+ + +Note: The plugin relies on JMagick, which ues JNI and native code. You need +to have the JMagick and ImageMagick shared libraries (or DLLs) in Java's +java.library.path for this plugin to work. + +

+See ImageMagick homepage +or JMagick homepage +for more information. diff --git a/twelvemonkeys-imageio/jmagick/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/twelvemonkeys-imageio/jmagick/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi new file mode 100755 index 00000000..c20afa0e --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi @@ -0,0 +1,18 @@ +com.twelvemonkeys.imageio.plugins.jmagick.BMPImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.GIFImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.ICOImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.JPEG2KImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.JPEGImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.PCDImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.PCXImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.PDBImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.PNGImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.PNMImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.PSDImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.SWFImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.TIFFImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.TargaImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.WBMPImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.WMFImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.XBMImageReaderSpi +com.twelvemonkeys.imageio.plugins.jmagick.XPMImageReaderSpi \ No newline at end of file diff --git a/twelvemonkeys-imageio/jmagick/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi b/twelvemonkeys-imageio/jmagick/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi new file mode 100755 index 00000000..9f4e0c54 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi @@ -0,0 +1,12 @@ +com.twelvemonkeys.imageio.plugins.jmagick.BMPImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.GIFImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.ICOImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.JPEG2KImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.JPEGImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.PCXImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.PNGImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.TIFFImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.TargaImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.WBMPImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.XBMImageWriterSpi +com.twelvemonkeys.imageio.plugins.jmagick.XPMImageWriterSpi \ No newline at end of file diff --git a/twelvemonkeys-imageio/jmagick/src/main/resources/com/twelvemonkeys/imageio/plugins/jmagick/JMagickReader.properties b/twelvemonkeys-imageio/jmagick/src/main/resources/com/twelvemonkeys/imageio/plugins/jmagick/JMagickReader.properties new file mode 100755 index 00000000..ec06786f --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/main/resources/com/twelvemonkeys/imageio/plugins/jmagick/JMagickReader.properties @@ -0,0 +1,6 @@ +# JMagick quirks mode +# ImageMagick/JMagick seems to use fileextension to recognize these formats... +TGA.useTempFile=true +WBMP.useTempFile=true +ICO.useTempFile=true +ILBM.useTempFile=true \ No newline at end of file diff --git a/twelvemonkeys-imageio/jmagick/src/test/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReaderTestCase.java b/twelvemonkeys-imageio/jmagick/src/test/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReaderTestCase.java new file mode 100755 index 00000000..11c3569e --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/test/java/com/twelvemonkeys/imageio/plugins/jmagick/BMPImageReaderTestCase.java @@ -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.jmagick; + +import javax.imageio.spi.ImageReaderSpi; +import java.awt.*; +import java.util.Arrays; +import java.util.List; + +public class BMPImageReaderTestCase extends JMagickImageReaderAbstractTestCase { + private BMPImageReaderSpi mProvider = new BMPImageReaderSpi(); + + protected List getTestData() { + return Arrays.asList( + new TestData(getClassLoaderResource("/bmp/Blue Lace 16.bmp"), new Dimension(48, 48)), + new TestData(getClassLoaderResource("/bmp/blauesglas_16.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_16_bitmask444.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_16_bitmask555.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_16_bitmask565.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_24.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_32.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_32_bitmask888.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_32_bitmask888_reversed.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_4-IM.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_4.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_4.rle"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_8-IM.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_8.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_8.rle"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_gray.bmp"), new Dimension(301, 331)), + new TestData(getClassLoaderResource("/bmp/blauesglas_mono.bmp"), new Dimension(301, 331)) + ); + } + + protected Class getReaderClass() { + return BMPImageReader.class; + } + + protected BMPImageReader createReader() { + return new BMPImageReader(mProvider); + } + + protected ImageReaderSpi createProvider() { + return new BMPImageReaderSpi(); + } + + protected List getFormatNames() { + return Arrays.asList("bmp"); + } + + protected List getSuffixes() { + return Arrays.asList("bmp", "rle", "dib"); + } + + protected List getMIMETypes() { + return Arrays.asList("image/bmp", "image/x-bmp", "image/x-windows-bmp", "image/x-ms-bmp"); + } + +} diff --git a/twelvemonkeys-imageio/jmagick/src/test/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageReaderAbstractTestCase.java b/twelvemonkeys-imageio/jmagick/src/test/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageReaderAbstractTestCase.java new file mode 100755 index 00000000..0c0d6376 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/src/test/java/com/twelvemonkeys/imageio/plugins/jmagick/JMagickImageReaderAbstractTestCase.java @@ -0,0 +1,44 @@ +/* + * 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 com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase; + +import javax.imageio.ImageReader; + +/** + * JMagickImageReaderAbstractTestCase + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: JMagickImageReaderAbstractTestCase.java,v 1.0 Apr 1, 2008 2:59:05 PM haraldk Exp$ + */ +public abstract class JMagickImageReaderAbstractTestCase extends ImageReaderAbstractTestCase { + +} diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/Blue Lace 16.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/Blue Lace 16.bmp new file mode 100755 index 00000000..680bc27a Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/Blue Lace 16.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16.bmp new file mode 100755 index 00000000..f1b09908 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask444.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask444.bmp new file mode 100755 index 00000000..584af94d Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask444.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask555.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask555.bmp new file mode 100755 index 00000000..f1b09908 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask555.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask565.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask565.bmp new file mode 100755 index 00000000..1bd00ff6 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_16_bitmask565.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_24.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_24.bmp new file mode 100755 index 00000000..903176f1 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_24.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32.bmp new file mode 100755 index 00000000..40f0b3d7 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32_bitmask888.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32_bitmask888.bmp new file mode 100755 index 00000000..fc06d157 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32_bitmask888.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32_bitmask888_reversed.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32_bitmask888_reversed.bmp new file mode 100755 index 00000000..71a5ed6e Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_32_bitmask888_reversed.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4-IM.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4-IM.bmp new file mode 100755 index 00000000..ca8db5ce Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4-IM.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4.bmp new file mode 100755 index 00000000..84012e48 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4.rle b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4.rle new file mode 100755 index 00000000..e0efa7c2 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_4.rle differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8-IM.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8-IM.bmp new file mode 100755 index 00000000..5a820296 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8-IM.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8.bmp new file mode 100755 index 00000000..2027b80c Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8.rle b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8.rle new file mode 100755 index 00000000..dea7e8d7 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_8.rle differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_gray.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_gray.bmp new file mode 100755 index 00000000..4a2a9483 Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_gray.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_mono.bmp b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_mono.bmp new file mode 100755 index 00000000..8c978c3f Binary files /dev/null and b/twelvemonkeys-imageio/jmagick/src/test/resources/bmp/blauesglas_mono.bmp differ diff --git a/twelvemonkeys-imageio/jmagick/todo.txt b/twelvemonkeys-imageio/jmagick/todo.txt new file mode 100755 index 00000000..1d35e265 --- /dev/null +++ b/twelvemonkeys-imageio/jmagick/todo.txt @@ -0,0 +1,5 @@ +- These readers/writers could probably benefit alot from using JNI with nio + bytechannel buffers... RFE to JMagick guys? +- Create stream providers and allow creatig a ImageInputStream from a + MagickImage? Does that even make sense? Or maybe create File based streams + that exposes the file name for JMagick to write directly. \ No newline at end of file diff --git a/twelvemonkeys-imageio/pict/license.txt b/twelvemonkeys-imageio/pict/license.txt new file mode 100755 index 00000000..033542bd --- /dev/null +++ b/twelvemonkeys-imageio/pict/license.txt @@ -0,0 +1,56 @@ +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. + + +Parts of this software is based on JVG/JIS. +See http://www.cs.hut.fi/~framling/JVG/index.html for more information. +Redistribution under BSD authorized by Kary Främling: + +Copyright (c) 2003, Kary Främling +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 of the JIS/JVG 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. \ No newline at end of file diff --git a/twelvemonkeys-imageio/pict/pom.xml b/twelvemonkeys-imageio/pict/pom.xml new file mode 100755 index 00000000..10c666da --- /dev/null +++ b/twelvemonkeys-imageio/pict/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + com.twelvemonkeys.imageio + twelvemonkeys-imageio-pict + 2.1 + TwelveMonkeys ImageIO PICT plugin + ImageIO plugin for Apple Mac Paint Picture (PICT) format. + + + twelvemonkeys-imageio + com.twelvemonkeys + 2.1 + + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + tests + + + \ No newline at end of file diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/BitMapPattern.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/BitMapPattern.java new file mode 100755 index 00000000..de6f80f2 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/BitMapPattern.java @@ -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.pict; + +import java.awt.*; +import java.awt.image.WritableRaster; +import java.awt.image.DataBufferByte; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; + +/** + * BitMapPattern + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: BitMapPattern.java,v 1.0 Mar 2, 2009 10:31:56 AM haraldk Exp$ + */ +final class BitMapPattern extends Pattern { + + BitMapPattern(final Paint pColor) { + super(pColor); + } + + public BitMapPattern(final byte[] pPattern) { + this(create8x8Pattern(pPattern)); + } + + BitMapPattern(final int pPattern) { + this(create8x8Pattern(pPattern)); + } + + private static TexturePaint create8x8Pattern(final int pPattern) { + // TODO: Creating a special purpose Pattern might be faster than piggy-backing on TexturePaint + WritableRaster raster = QuickDraw.MONOCHROME.createCompatibleWritableRaster(8, 8); + byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData(); + + for (int i = 0; i < data.length; i += 4) { + data[i ] = (byte) ((pPattern >> 24) & 0xFF); + data[i + 1] = (byte) ((pPattern >> 16) & 0xFF); + data[i + 2] = (byte) ((pPattern >> 8) & 0xFF); + data[i + 3] = (byte) ((pPattern ) & 0xFF); + } + + BufferedImage img = new BufferedImage(QuickDraw.MONOCHROME, raster, false, null); + return new TexturePaint(img, new Rectangle(8, 8)); + } + + private static TexturePaint create8x8Pattern(final byte[] pPattern) { + WritableRaster raster = Raster.createPackedRaster(new DataBufferByte(pPattern, 8), 8, 8, 1, new Point()); + BufferedImage img = new BufferedImage(QuickDraw.MONOCHROME, raster, false, null); + return new TexturePaint(img, new Rectangle(8, 8)); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICT.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICT.java new file mode 100755 index 00000000..eca3c6a4 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICT.java @@ -0,0 +1,178 @@ +/* + * 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.pict; + +/** + * PICT format constants. + *

+ * + * @author Harald Kuhr + * @version $Id: PICT.java,v 1.0 06.apr.2006 12:53:17 haku Exp$ + */ +interface PICT { + /** PICT V1 identifier, two bytes, mask with 0xffff0000 */ + int MAGIC_V1 = 0x11010000; + /** PICT V2 identifier, four bytes */ + int MAGIC_V2 = 0x001102ff; + + int PICT_NULL_HEADER_SIZE = 512; + + // V2 Header, -1 (int) + int HEADER_V2 = 0xffffffff; + // V2 Extended header, -2 (short) + reserved (short) + int HEADER_V2_EXT = 0xfffe0000; + + // PICT/QuickDraw uses 16 bit precision per color component internally + int COLOR_COMP_SIZE = 2; + + /** Default Apple Macintosh DPI setting (72 DPI). */ + int MAC_DEFAULT_DPI = 72; + + /** + * PICT opcodes. + */ + int OP_HEADER_OP = 0x0C00; + int NOP = 0x00; + int OP_CLIP_RGN = 0x01; + int OP_BK_PAT = 0x02; + int OP_TX_FONT = 0x03; + int OP_TX_FACE = 0x04; + int OP_TX_MODE = 0x05; + int OP_SP_EXTRA = 0x06; + int OP_PN_SIZE = 0x07; + int OP_PN_MODE = 0x08; + int OP_PN_PAT = 0x09; + int OP_FILL_PAT = 0x0A; + int OP_OV_SIZE = 0x0B; + int OP_ORIGIN = 0x0C; + int OP_TX_SIZE = 0x0D; + int OP_FG_COLOR = 0x0E; + int OP_BK_COLOR = 0x0F; + int OP_TX_RATIO = 0x10; + int OP_VERSION = 0x11; + /* Not implemented */ + int OP_BK_PIX_PAT = 0x12; + int OP_PN_PIX_PAT = 0x13; + int OP_FILL_PIX_PAT = 0x14; + int OP_PN_LOC_H_FRAC = 0x15; + int OP_CH_EXTRA = 0x16; + int OP_RGB_FG_COL = 0x1A; + int OP_RGB_BK_COL = 0x1B; + int OP_HILITE_MODE = 0x1C; + int OP_HILITE_COLOR = 0x1D; + int OP_DEF_HILITE = 0x1E; + int OP_OP_COLOR = 0x1F; + int OP_LINE = 0x20; + int OP_LINE_FROM = 0x21; + int OP_SHORT_LINE = 0x22; + int OP_SHORT_LINE_FROM = 0x23; + int OP_LONG_TEXT = 0x28; + int OP_DH_TEXT = 0x29; + int OP_DV_TEXT = 0x2A; + int OP_DHDV_TEXT = 0x2B; + int OP_FONT_NAME = 0x2C; + int OP_LINE_JUSTIFY = 0x2D; + int OP_GLYPH_STATE = 0x2E; + int OP_FRAME_RECT = 0x30; + int OP_PAINT_RECT = 0x31; + int OP_ERASE_RECT = 0x32; + int OP_INVERT_RECT = 0x33; + int OP_FILL_RECT = 0x34; + int OP_FRAME_SAME_RECT = 0x38; + int OP_PAINT_SAME_RECT = 0x39; + int OP_ERASE_SAME_RECT = 0x3A; + int OP_INVERT_SAME_RECT = 0x3B; + int OP_FILL_SAME_RECT = 0x3C; + int OP_FRAME_R_RECT = 0x40; + int OP_PAINT_R_RECT = 0x41; + int OP_ERASE_R_RECT = 0x42; + int OP_INVERT_R_RECT = 0x43; + int OP_FILL_R_RECT = 0x44; + int OP_FRAME_SAME_R_RECT = 0x48; + int OP_PAINT_SAME_R_RECT = 0x49; + int OP_ERASE_SAME_R_RECT = 0x4A; + int OP_INVERT_SAME_R_RECT = 0x4B; + int OP_FILL_SAME_R_RECT = 0x4C; + int OP_FRAME_OVAL = 0x50; + int OP_PAINT_OVAL = 0x51; + int OP_ERASE_OVAL = 0x52; + int OP_INVERT_OVAL = 0x53; + int OP_FILL_OVAL = 0x54; + int OP_FRAME_SAME_OVAL = 0x58; + int OP_PAINT_SAME_OVAL = 0x59; + int OP_ERASE_SAME_OVAL = 0x5A; + int OP_INVERT_SAME_OVAL = 0x5B; + int OP_FILL_SAME_OVAL = 0x5C; + int OP_FRAME_ARC = 0x60; + int OP_PAINT_ARC = 0x61; + int OP_ERASE_ARC = 0x62; + int OP_INVERT_ARC = 0x63; + int OP_FILL_ARC = 0x64; + int OP_FRAME_SAME_ARC = 0x68; + int OP_PAINT_SAME_ARC = 0x69; + int OP_ERASE_SAME_ARC = 0x6A; + int OP_INVERT_SAME_ARC = 0x6B; + int OP_FILL_SAME_ARC = 0x6C; + int OP_FRAME_POLY = 0x70; + int OP_PAINT_POLY = 0x71; + int OP_ERASE_POLY = 0x72; + int OP_INVERT_POLY = 0x73; + int OP_FILL_POLY = 0x74; + int OP_FRAME_SAME_POLY = 0x78; + int OP_PAINT_SAME_POLY = 0x79; + int OP_ERASE_SAME_POLY = 0x7A; + int OP_INVERT_SAME_POLY = 0x7B; + int OP_FILL_SAME_POLY = 0x7C; + int OP_FRAME_RGN = 0x80; + int OP_PAINT_RGN = 0x81; + int OP_ERASE_RGN = 0x82; + int OP_INVERT_RGN = 0x83; + int OP_FILL_RGN = 0x84; + int OP_FRAME_SAME_RGN = 0x88; + int OP_PAINT_SAME_RGN = 0x89; + int OP_ERASE_SAME_RGN = 0x8A; + int OP_INVERT_SAME_RGN = 0x8B; + int OP_FILL_SAME_RGN = 0x8C; + /* Not implemented */ + int OP_BITS_RECT = 0x90; + int OP_BITS_RGN = 0x91; + int OP_PACK_BITS_RECT = 0x98; + int OP_PACK_BITS_RGN = 0x99; + int OP_DIRECT_BITS_RECT = 0x9A; + /* Not implemented */ + int OP_DIRECT_BITS_RGN = 0x9B; + int OP_SHORT_COMMENT = 0xA0; + int OP_LONG_COMMENT = 0xA1; + int OP_END_OF_PICTURE = 0xFF; + int OP_VERSION_2 = 0x2FF; + int OP_COMPRESSED_QUICKTIME = 0x8200; + int OP_UNCOMPRESSED_QUICKTIME = 0x8201; + + String APPLE_USE_RESERVED_FIELD = "Reserved for Apple use."; +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java new file mode 100755 index 00000000..a05c775d --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java @@ -0,0 +1,2797 @@ +/* +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. + + +Parts of this software is based on JVG/JIS. +See http://www.cs.hut.fi/~framling/JVG/index.html for more information. +Redistribution under BSD authorized by Kary Främling: + +Copyright (c) 2003, Kary Främling +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 of the JIS/JVG 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.pict; + +import com.twelvemonkeys.imageio.ImageReaderBase; +import com.twelvemonkeys.imageio.util.IIOUtil; +import com.twelvemonkeys.io.enc.Decoder; +import com.twelvemonkeys.io.enc.DecoderStream; +import com.twelvemonkeys.io.enc.PackBits16Decoder; +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.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.image.*; +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * Reader for Apple Mac Paint Picture (PICT) format. + *

+ * + * @author Harald Kuhr + * @author Kary Främling (original PICT/QuickDraw parsing) + * @author Matthias Wiesmann (original embedded QuickTime parsing) + * @version $Id: PICTReader.java,v 1.0 05.apr.2006 15:20:48 haku Exp$ + */ +/* + * @todo New paint strategy: Need to have a PEN and a PEN MODE, in addition to BG and PATTERN and PATTERN MODE. + * - These must be set before each frame/paint/invert/erase/fill operation. + * This is because there isn't a one-to-one mapping, between Java and PICT drawing. + * - Subclass Graphics? + * - Or create a QuickDrawContext that converts from PICT to AWT Graphics? + * - Or Methods like setupFrame(pen, penmode, penwidth?), setupPaint(pen, penmode), setupFill(patter, patMode), etc? + * - Or methods like frameRect(pen, penmode, penwidth, rect), frameOval(pen, penmode, penwidth, rect), etc? + * - Or methods like frameShape(pen, penmode, penwidth, shape), paintShape(pen, penmode, shape) etc?? + * QuickDrawContext that wraps an AWT Grpahics, and with methods macthing opcodes, seems like the best fit ATM + * @todo Remove null-checks for Graphics, as null-graphics makes no sense. + * @todo Some MAJOR clean up + * @todo Object orientation of different opcodes? + * @todo As we now have Graphics2D with more options, support more of the format? + * @todo Support for some other compression (packType 3) that seems to be common... + */ +public class PICTImageReader extends ImageReaderBase { + + static boolean DEBUG = false; + + // Private fields + private QuickDrawContext mContext; + private Rectangle mFrame; + + private int mVersion; + + // Variables for storing draw status + private Point mPenPosition = new Point(0, 0); + private Rectangle mLastRectangle = new Rectangle(0, 0); + + // Ratio between the screen resolution and the image resolution + private double mScreenImageXRatio; + private double mScreenImageYRatio; + + // List of images created during image import + private List mImages = new ArrayList(); + private long mImageStartStreamPos; + protected int mPicSize; + + public PICTImageReader() { + this(null); + } + + protected PICTImageReader(final ImageReaderSpi pProvider) { + super(pProvider); + } + + protected void resetMembers() { + mContext = null; + mFrame = null; + mImages.clear(); + } + + /** + * Open and read the frame size of the PICT file. + * + * @return return the PICT frame + * @throws IOException if an I/O error occurs while reading the image. + */ + private Rectangle getPICTFrame() throws IOException { + if (mFrame == null) { + // Read in header information + readPICTHeader(mImageInput); + if (DEBUG) { + System.out.println("Done reading PICT header!"); + } + } + return mFrame; + } + + /** + * Read the PICT header. The information read is shown on stdout if "DEBUG" is true. + * + * @param pStream the stream to read from + * + * @throws IOException if an I/O error occurs while reading the image. + */ + private void readPICTHeader(final ImageInputStream pStream) throws IOException { + pStream.seek(0l); + + try { + readPICTHeader0(pStream); + } + catch (IIOException e) { + // Rest and try again + pStream.seek(0l); + + // Skip first 512 bytes + skipNullHeader(pStream); + readPICTHeader0(pStream); + } + } + + private void readPICTHeader0(final ImageInputStream pStream) throws IOException { + // Get size + mPicSize = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.println("picSize: " + mPicSize); + } + + // Get frame at 72 dpi + // NOTE: These are not pixel sizes! + // Need sto be multiplied with hRes/screenResolution and vRes/screenResolution + int y = pStream.readUnsignedShort(); + int x = pStream.readUnsignedShort(); + int h = pStream.readUnsignedShort(); + int w = pStream.readUnsignedShort(); + + mFrame = new Rectangle(x, y, w - x, h - y); + if (mFrame.width < 0 || mFrame.height < 0) { + throw new IIOException("Error in PICT header: Invalid frame " + mFrame); + } + if (DEBUG) { + System.out.println("mFrame: " + mFrame); + } + + // Set default display ratios. 72 dpi is the standard Macintosh resolution. + mScreenImageXRatio = 1.0; + mScreenImageYRatio = 1.0; + + // Get the version, since the way of reading the rest depends on it + boolean isExtendedV2 = false; + int version = pStream.readShort(); + if (DEBUG) { + System.out.println(String.format("PICT version: 0x%04x", version)); + } + + if (version == (PICT.OP_VERSION << 8) + 0x01) { + mVersion = 1; + } + else if (version == PICT.OP_VERSION && pStream.readShort() == PICT.OP_VERSION_2) { + mVersion = 2; + + // Read in version 2 header op and test that it is valid: HeaderOp 0x0C00 + if (pStream.readShort() != PICT.OP_HEADER_OP) { + throw new IIOException("Error in PICT header: Invalid HeaderOp, expected 0x0c00"); + } + + int headerVersion = pStream.readInt(); + if (DEBUG) { + System.out.println(String.format("headerVersion: 0x%04x", headerVersion)); + } + + // TODO: This (headerVersion) should be picture size (bytes) for non-V2-EXT...? + // - but.. We should take care to make sure we don't mis-interpret non-PICT data... + //if (headerVersion == PICT.HEADER_V2) { + if ((headerVersion & 0xffff0000) != PICT.HEADER_V2_EXT) { + // TODO: Test this.. Looks dodgy to me.. + // Get the image resolution and calculate the ratio between + // the default Mac screen resolution and the image resolution + + // int y (fixed point) + double y2 = PICTUtil.readFixedPoint(pStream); + // int x (fixed point) + double x2 = PICTUtil.readFixedPoint(pStream); + // int w (fixed point) + double w2 = PICTUtil.readFixedPoint(pStream); // ?! + // int h (fixed point) + double h2 = PICTUtil.readFixedPoint(pStream); + + mScreenImageXRatio = (w - x) / (w2 - x2); + mScreenImageYRatio = (h - y) / (h2 - y2); + + if (mScreenImageXRatio < 0 || mScreenImageYRatio < 0) { + throw new IIOException("Error in PICT header: Invalid bounds " + new Rectangle.Double(x2, y2, w2 - x2, h2 - y2)); + } + if (DEBUG) { + System.out.println("bounding rect: " + new Rectangle.Double(x2, y2, w2 - x2, h2 - y2)); + } + + // int reserved + pStream.skipBytes(4); + } + else /*if ((headerVersion & 0xffff0000) == PICT.HEADER_V2_EXT)*/ { + isExtendedV2 = true; + // Get the image resolution + // Not sure if they are useful for anything... + + // int horizontal res (fixed point) + double xRes = PICTUtil.readFixedPoint(pStream); + // int vertical res (fixed point) + double yRes = PICTUtil.readFixedPoint(pStream); + + if (DEBUG) { + System.out.println("xResolution: " + xRes); + System.out.println("yResolution: " + yRes); + } + + // Get the image resolution and calculate the ratio between + // the default Mac screen resolution and the image resolution + // short y + short y2 = pStream.readShort(); + // short x + short x2 = pStream.readShort(); + // short h + short h2 = pStream.readShort(); + // short w + short w2 = pStream.readShort(); + + mScreenImageXRatio = (w - x) / (double) (w2 - x2); + mScreenImageYRatio = (h - y) / (double) (h2 - y2); + + if (mScreenImageXRatio < 0 || mScreenImageYRatio < 0) { + throw new IIOException("Error in PICT header: Invalid bounds " + new Rectangle.Double(x2, y2, w2 - x2, h2 - y2)); + } + if (DEBUG) { + System.out.println("bounding rect: " + new Rectangle(x2, y2, w2 - x2, h2 - y2)); + } + + // long reserved + pStream.skipBytes(4); + } + + if (DEBUG) { + System.out.println("screenImageXRatio: " + mScreenImageXRatio); + System.out.println("screenImageYRatio: " + mScreenImageYRatio); + } + } + else { + // No version information, return straight away + throw new IIOException("Error in PICT header: Missing or unknown version information"); + } + + if (DEBUG) { + System.out.println("Version: " + mVersion + (isExtendedV2 ? " extended" : "")); + } + + mImageStartStreamPos = pStream.getStreamPosition(); + + // Won't need header data again (NOTE: We'll only get here if no exception is thrown) + pStream.flushBefore(mImageStartStreamPos); + } + + static void skipNullHeader(final ImageInputStream pStream) throws IOException { + // NOTE: Only skip if FILE FORMAT, not needed for Mac OS DnD + // Spec says "platofrm dependent", may not be all nulls.. + pStream.skipBytes(PICT.PICT_NULL_HEADER_SIZE); + } + + /** + * Reads the PICT stream. + * The contents of the stream will be drawn onto the supplied graphics + * object. + *

+ * If "DEBUG" is true, the elements read are listed on stdout. + * + * @param pGraphics the graphics object to draw onto. + * + * @throws javax.imageio.IIOException if the data can not be read. + * @throws IOException if an I/O error occurs while reading the image. + */ + private void drawOnto(Graphics2D pGraphics) throws IOException { + mContext = new QuickDrawContext(pGraphics); + + readPICTopcodes(mImageInput); + if (DEBUG) { + System.out.println("Done reading PICT body!"); + } + } + + /** + * Parse PICT opcodes in a PICT file. The input stream must be + * positioned at the beginning of the opcodes, after picframe. + * If we have a non-null graphics, we try to draw the elements. + * + * @param pStream the stream to read from + * + * @throws javax.imageio.IIOException if the data can not be read. + * @throws java.io.IOException if an I/O error occurs while reading the image. + */ + private void readPICTopcodes(ImageInputStream pStream) throws IOException { + pStream.seek(mImageStartStreamPos); + + int opCode, dh, dv, dataLength; + byte[] colorBuffer = new byte[3 * PICT.COLOR_COMP_SIZE]; + + + Pattern fill = QuickDraw.BLACK; + Pattern bg; + Pattern pen; + Paint foreground; + Paint background; + Color hilight = Color.RED; + + Point origin, dh_dv; + Point ovSize = new Point(); + Point arcAngles = new Point(); + String text; + Rectangle bounds = new Rectangle(); + Polygon polygon = new Polygon(); + Polygon region = new Polygon(); + int pixmapCount = 0; + + try { + // Read from file until we read the end of picture opcode + do { + // Read opcode, version 1: byte, version 2: short + if (mVersion == 1) { + opCode = pStream.readUnsignedByte(); + } + else { + // Always word-aligned for version 2 + if ((pStream.getStreamPosition() & 1) > 0) { + pStream.readByte(); + } + opCode = pStream.readUnsignedShort(); + } + + // See what we got and react in consequence + switch (opCode) { + case PICT.NOP: + // Just go on + if (DEBUG) { + System.out.println("NOP"); + } + break; + + case PICT.OP_CLIP_RGN:// OK for RECTS, not for regions yet + // Read the region + if ((region = readRegion(pStream, bounds)) == null) { + throw new IIOException("Could not read region"); + } + // Set clip rect or clip region + + //if (mGraphics != null) { + // if (region.npoints == 0) { + // // TODO: Read what the specs says about this... + // if (bounds.width > 0 && bounds.height > 0) { + // mGraphics.setClip(bounds.x, bounds.y, bounds.width, bounds.height); + // } + // } + // else { + // mGraphics.setClip(region); + // } + //} + if (DEBUG) { + verboseRegionCmd("clipRgn", bounds, region); + } + break; + + case PICT.OP_BK_PAT: + // Get the data + mContext.setBackgroundPattern(PICTUtil.readPattern(pStream)); + if (DEBUG) { + System.out.println("bkPat"); + } + break; + + case PICT.OP_TX_FONT:// DIFFICULT TO KNOW THE FONT??? + // Get the data + pStream.readFully(new byte[2], 0, 2); + // TODO: Font family id, 0 - System font, 1 - Application font. + // But how can we get these mappings? + if (DEBUG) { + System.out.println("txFont"); + } + break; + + case PICT.OP_TX_FACE:// SEE IF IT IS TO BE IMPLEMENTED FOR NOW? + // Get the data + byte txFace = pStream.readByte(); + + //// Construct text face mask +// currentFont = mGraphics.getFont(); + //int awt_face_mask = 0; + //if ((txFace & (byte) QuickDraw.TX_BOLD_MASK) > 0) { + // awt_face_mask |= Font.BOLD; + //} + //if ((txFace & (byte) QuickDraw.TX_ITALIC_MASK) > 0) { + // awt_face_mask |= Font.ITALIC; + //} + // + //// Set the font + //mGraphics.setFont(new Font(currentFont.getName(), awt_face_mask, currentFont.getSize())); + + if (DEBUG) { + System.out.println("txFace: " + txFace); + } + break; + + case PICT.OP_TX_MODE:// SEE IF IT IS TO BE IMPLEMENTED FOR NOW? + // Get the data + byte[] mode_buf = new byte[2]; + pStream.readFully(mode_buf, 0, mode_buf.length); + if (DEBUG) { + System.out.println("txMode: " + mode_buf[0] + ", " + mode_buf[1]); + } + break; + + case PICT.OP_SP_EXTRA:// WONDER WHAT IT IS? + // Get the data + pStream.readFully(new byte[4], 0, 4); + if (DEBUG) { + System.out.println("spExtra"); + } + break; + + case PICT.OP_PN_SIZE: + // Get the two words + // NOTE: This is out of order, compared to other Points + Dimension pnsize = new Dimension(pStream.readUnsignedShort(), pStream.readUnsignedShort()); + mContext.setPenSize(pnsize); + if (DEBUG) { + System.out.println("pnsize: " + pnsize); + } + break; + + case PICT.OP_PN_MODE:// TRY EMULATING WITH SETXORMODE ETC + // Get the data + int mode = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.println("pnMode: " + mode); + } + + mContext.setPenMode(mode); + + break; + + case PICT.OP_PN_PAT: + mContext.setPenPattern(PICTUtil.readPattern(pStream)); + if (DEBUG) { + System.out.println("pnPat"); + } + break; + + case PICT.OP_FILL_PAT: + fill = PICTUtil.readPattern(pStream); + if (DEBUG) { + System.out.println("fillPat"); + } + break; + + case PICT.OP_OV_SIZE:// OK, we use this for rounded rectangle corners + // Get the two words + int y = getYPtCoord(pStream.readUnsignedShort()); + int x = getXPtCoord(pStream.readUnsignedShort()); + + ovSize.setLocation(x, y); + /* + ovSize.x *= 2;// Don't know why, but has to be multiplied by 2 + + ovSize.y *= 2; + */ + if (DEBUG) { + System.out.println("ovSize: " + ovSize); + } + break; + + case PICT.OP_ORIGIN:// PROBABLY OK + // Get the two words + y = getYPtCoord(pStream.readUnsignedShort()); + x = getXPtCoord(pStream.readUnsignedShort()); + origin = new Point(x, y); + //if (mGraphics != null) { + // mGraphics.translate(origin.x, origin.y); + //} + if (DEBUG) { + System.out.println("Origin: " + origin); + } + break; + + case PICT.OP_TX_SIZE:// OK + // Get the text size + int tx_size = getYPtCoord(pStream.readUnsignedShort()); + //if (mGraphics != null) { + // currentFont = mGraphics.getFont(); + // mGraphics.setFont(new Font(currentFont.getName(), currentFont.getStyle(), tx_size)); + //} + mContext.setTextSize(tx_size); + if (DEBUG) { + System.out.println("txSize: " + tx_size); + } + break; + + case PICT.OP_FG_COLOR:// TO BE DONE IF POSSIBLE + // TODO! + // Get the data + pStream.readInt(); + if (DEBUG) { + System.out.println("fgColor"); + } + break; + + case PICT.OP_BK_COLOR:// TO BE DONE IF POSSIBLE + // TODO! + // Get the data + pStream.readInt(); + if (DEBUG) { + System.out.println("bgColor"); + } + break; + + case PICT.OP_TX_RATIO:// SEE IF WE HAVE THIS??? + // Get the data + pStream.readFully(new byte[8], 0, 8); + if (DEBUG) { + System.out.println("txRatio"); + } + break; + + case PICT.OP_VERSION:// OK, ignored since we should already have it + // Get the data + pStream.readFully(new byte[1], 0, 1); + if (DEBUG) { + System.out.println("opVersion"); + } + break; + + case 0x0012: // BkPixPat + bg = PICTUtil.readColorPattern(pStream); + mContext.setBackgroundPattern(bg); + break; + case 0x0013: // PnPixPat + pen = PICTUtil.readColorPattern(pStream); + mContext.setBackgroundPattern(pen); + break; + case 0x0014: // FillPixPat + fill = PICTUtil.readColorPattern(pStream); + mContext.setBackgroundPattern(fill); + break; + + case PICT.OP_PN_LOC_H_FRAC:// TO BE DONE??? + // Get the data + pStream.readFully(new byte[2], 0, 2); + if (DEBUG) { + System.out.println("opPnLocHFrac"); + } + break; + + case PICT.OP_CH_EXTRA:// TO BE DONE??? + // Get the data + pStream.readFully(new byte[2], 0, 2); + if (DEBUG) { + System.out.println("opChExtra"); + } + break; + + case PICT.OP_RGB_FG_COL:// OK + // Get the color + pStream.readFully(colorBuffer, 0, colorBuffer.length); + foreground = new Color((colorBuffer[0] & 0xFF), (colorBuffer[2] & 0xFF), (colorBuffer[4] & 0xFF)); + //if (mGraphics != null) { + // mGraphics.setColor(foreground); + //} + if (DEBUG) { + System.out.println("rgbFgColor: " + foreground); + } + break; + + case PICT.OP_RGB_BK_COL:// OK + // Get the color + pStream.readFully(colorBuffer, 0, colorBuffer.length); + // TODO: The color might be 16 bit per component.. + background = new Color((colorBuffer[0] & 0xFF), (colorBuffer[2] & 0xFF), (colorBuffer[4] & 0xFF)); + if (DEBUG) { + System.out.println("rgbBgColor: " + background); + } + break; + + case PICT.OP_HILITE_MODE: + // Change color to hilite color + mContext.setPenPattern(new BitMapPattern(hilight)); + if (DEBUG) { + System.out.println("opHiliteMode"); + } + break; + + case PICT.OP_HILITE_COLOR:// OK + // Get the color + pStream.readFully(colorBuffer, 0, colorBuffer.length); + // TODO: The color might be 16 bit per component.. + hilight = new Color((colorBuffer[0] & 0xFF), (colorBuffer[2] & 0xFF), (colorBuffer[4] & 0xFF)); + if (DEBUG) { + System.out.println("opHiliteColor: " + hilight); + } + break; + + case PICT.OP_DEF_HILITE:// Macintosh internal, ignored? + // Nothing to do + hilight = Color.red; // TODO: My guess it's a reset, verify! + if (DEBUG) { + System.out.println("opDefHilite"); + } + break; + + case PICT.OP_OP_COLOR:// To be done once I know what it means + // TODO: Is this the mask? Scale value for RGB colors? + // Get the color + pStream.readFully(colorBuffer, 0, colorBuffer.length); + if (DEBUG) { + System.out.println("opOpColor"); + } + break; + + case PICT.OP_LINE:// OK, not tested + // Get the data (two points) + y = getYPtCoord(pStream.readUnsignedShort()); + x = getXPtCoord(pStream.readUnsignedShort()); + origin = new Point(x, y); + + y = getYPtCoord(pStream.readUnsignedShort()); + x = getXPtCoord(pStream.readUnsignedShort()); + mPenPosition.setLocation(x, y); + + // Move pen to new position, draw line + mContext.moveTo(origin); + mContext.lineTo(mPenPosition); + + if (DEBUG) { + System.out.println("line from: " + origin + " to: " + mPenPosition); + } + break; + + case PICT.OP_LINE_FROM:// OK, not tested + // Get the point + y = getYPtCoord(pStream.readUnsignedShort()); + x = getXPtCoord(pStream.readUnsignedShort()); + + // Draw line + mContext.line(x, y); + + if (DEBUG) { + System.out.println("lineFrom to: " + mPenPosition); + } + break; + + case PICT.OP_SHORT_LINE:// OK + // Get origin and dh, dv + y = getYPtCoord(pStream.readUnsignedShort()); + x = getXPtCoord(pStream.readUnsignedShort()); + origin = new Point(x, y); + + y = getYPtCoord(pStream.readByte()); + x = getXPtCoord(pStream.readByte()); + dh_dv = new Point(x, y); + + // Move pen to new position, draw line if we have a graphics + mPenPosition.setLocation(origin.x + dh_dv.x, origin.y + dh_dv.y); + mContext.lineTo(mPenPosition); + + if (DEBUG) { + System.out.println("Short line origin: " + origin + ", dh,dv: " + dh_dv); + } + break; + + case PICT.OP_SHORT_LINE_FROM:// OK + // Get dh, dv + y = getYPtCoord(pStream.readByte()); + x = getXPtCoord(pStream.readByte()); + + // Draw line + mContext.line(x, y); + + if (DEBUG) { + System.out.println("Short line from dh,dv: " + x + "," + y); + } + break; + + case 0x24: + case 0x25: + case 0x26: + case 0x27: + // Apple reserved + dataLength = pStream.readUnsignedShort(); + + pStream.readFully(new byte[dataLength], 0, dataLength); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + case PICT.OP_LONG_TEXT:// OK + // Get the data + y = getYPtCoord(pStream.readUnsignedShort()); + x = getXPtCoord(pStream.readUnsignedShort()); + origin = new Point(x, y); + mPenPosition = origin; + mContext.moveTo(mPenPosition); + text = PICTUtil.readPascalString(pStream); + // TODO + //if (mGraphics != null) { + // mGraphics.drawString(text, mPenPosition.x, mPenPosition.y); + //} + mContext.drawString(text); + if (DEBUG) { + System.out.println("longText origin: " + mPenPosition + ", text:" + text); + } + break; + + case PICT.OP_DH_TEXT:// OK, not tested + // Get dh + dh = getXPtCoord(pStream.readByte()); + mPenPosition.translate(dh, 0); + mContext.moveTo(mPenPosition); + text = PICTUtil.readPascalString(pStream); + // TODO +// if (mGraphics != null) { +// mGraphics.drawString(text, mPenPosition.x, mPenPosition.y); +// } + mContext.drawString(text); + if (DEBUG) { + System.out.println("DHText dh: " + dh + ", text:" + text); + } + break; + + case PICT.OP_DV_TEXT:// OK, not tested + // Get dh + dv = getYPtCoord(pStream.readByte()); + mPenPosition.translate(0, dv); + mContext.moveTo(mPenPosition); + text = PICTUtil.readPascalString(pStream); + // TODO + //if (mGraphics != null) { + // mGraphics.drawString(text, mPenPosition.x, mPenPosition.y); + //} + mContext.drawString(text); + if (DEBUG) { + System.out.println("DVText dv: " + dv + ", text:" + text); + } + break; + + case PICT.OP_DHDV_TEXT:// OK, not tested + // Get dh, dv + y = getYPtCoord(pStream.readByte()); + x = getXPtCoord(pStream.readByte()); + mPenPosition.translate(x, y); + mContext.moveTo(mPenPosition); + text = PICTUtil.readPascalString(pStream); + // TODO + //if (mGraphics != null) { + // mGraphics.drawString(text, mPenPosition.x, mPenPosition.y); + //} + mContext.drawString(text); + if (DEBUG) { + System.out.println("DHDVText penPosition: " + mPenPosition + ", text:" + text); + } + break; + + case PICT.OP_FONT_NAME:// OK, not tested + // Get data length + /*data_len = */ + pStream.readShort(); + + // Get old font ID, ignored +// pStream.readInt(); + pStream.readUnsignedShort(); + + // Get font name and set the new font if we have one + text = PICTUtil.readPascalString(pStream); + // TODO + //if (mGraphics != null) { + // mGraphics.setFont(Font.decode(text) + // .deriveFont(currentFont.getStyle(), currentFont.getSize())); + //} + mContext.drawString(text); + if (DEBUG) { + System.out.println("fontName: \"" + text +"\""); + } + break; + + case PICT.OP_LINE_JUSTIFY:// TO BE DONE??? + // Get data + pStream.readFully(new byte[10], 0, 10); + if (DEBUG) { + System.out.println("opLineJustify"); + } + break; + + case PICT.OP_GLYPH_STATE:// TODO: NOT SUPPORTED IN AWT GRAPHICS YET? + // Get data + pStream.readFully(new byte[6], 0, 6); + if (DEBUG) { + System.out.println("glyphState"); + } + break; + + case 0x2F: + dataLength = pStream.readUnsignedShort(); + pStream.readFully(new byte[dataLength], 0, dataLength); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + //-------------------------------------------------------------------------------- + // Rect treatments + //-------------------------------------------------------------------------------- + case PICT.OP_FRAME_RECT:// OK + case PICT.OP_PAINT_RECT:// OK + case PICT.OP_ERASE_RECT:// OK, not tested + case PICT.OP_INVERT_RECT:// OK, not tested + case PICT.OP_FILL_RECT:// OK, not tested + // Get the frame rectangle + readRectangle(pStream, mLastRectangle); + + case PICT.OP_FRAME_SAME_RECT:// OK, not tested + case PICT.OP_PAINT_SAME_RECT:// OK, not tested + case PICT.OP_ERASE_SAME_RECT:// OK, not tested + case PICT.OP_INVERT_SAME_RECT:// OK, not tested + case PICT.OP_FILL_SAME_RECT:// OK, not tested + // Draw + switch (opCode) { + case PICT.OP_FRAME_RECT: + case PICT.OP_FRAME_SAME_RECT: + mContext.frameRect(mLastRectangle); + break; + case PICT.OP_PAINT_RECT: + case PICT.OP_PAINT_SAME_RECT: + mContext.paintRect(mLastRectangle); + break; + case PICT.OP_ERASE_RECT: + case PICT.OP_ERASE_SAME_RECT: + mContext.eraseRect(mLastRectangle); + break; + case PICT.OP_INVERT_RECT: + case PICT.OP_INVERT_SAME_RECT: + mContext.invertRect(mLastRectangle); + break; + case PICT.OP_FILL_RECT: + case PICT.OP_FILL_SAME_RECT: + mContext.fillRect(mLastRectangle, fill); + break; + } + + // Do verbose mode output + if (DEBUG) { + switch (opCode) { + case PICT.OP_FRAME_RECT: + System.out.println("frameRect: " + mLastRectangle); + break; + case PICT.OP_PAINT_RECT: + System.out.println("paintRect: " + mLastRectangle); + break; + case PICT.OP_ERASE_RECT: + System.out.println("eraseRect: " + mLastRectangle); + break; + case PICT.OP_INVERT_RECT: + System.out.println("invertRect: " + mLastRectangle); + break; + case PICT.OP_FILL_RECT: + System.out.println("fillRect: " + mLastRectangle); + break; + case PICT.OP_FRAME_SAME_RECT: + System.out.println("frameSameRect: " + mLastRectangle); + break; + case PICT.OP_PAINT_SAME_RECT: + System.out.println("paintSameRect: " + mLastRectangle); + break; + case PICT.OP_ERASE_SAME_RECT: + System.out.println("eraseSameRect: " + mLastRectangle); + break; + case PICT.OP_INVERT_SAME_RECT: + System.out.println("invertSameRect: " + mLastRectangle); + break; + case PICT.OP_FILL_SAME_RECT: + System.out.println("fillSameRect: " + mLastRectangle); + break; + } + } + + // Rect treatments finished + break; + + case 0x003d: + case 0x003e: + case 0x003f: + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + //-------------------------------------------------------------------------------- + // Round Rect treatments + //-------------------------------------------------------------------------------- + case PICT.OP_FRAME_R_RECT:// OK + case PICT.OP_PAINT_R_RECT:// OK, not tested + case PICT.OP_ERASE_R_RECT:// OK, not tested + case PICT.OP_INVERT_R_RECT:// OK, not tested + case PICT.OP_FILL_R_RECT:// OK, not tested + // Get the frame rectangle + readRectangle(pStream, mLastRectangle); + + case PICT.OP_FRAME_SAME_R_RECT:// OK, not tested + case PICT.OP_PAINT_SAME_R_RECT:// OK, not tested + case PICT.OP_ERASE_SAME_R_RECT:// OK, not tested + case PICT.OP_INVERT_SAME_R_RECT:// OK, not tested + case PICT.OP_FILL_SAME_R_RECT:// OK, not tested + // Draw + switch (opCode) { + case PICT.OP_FRAME_R_RECT: + case PICT.OP_FRAME_SAME_R_RECT: + mContext.frameRoundRect(mLastRectangle, ovSize.x, ovSize.y); + break; + case PICT.OP_PAINT_R_RECT: + case PICT.OP_PAINT_SAME_R_RECT: + mContext.paintRoundRect(mLastRectangle, ovSize.x, ovSize.y); + break; + case PICT.OP_ERASE_R_RECT: + case PICT.OP_ERASE_SAME_R_RECT: + mContext.eraseRoundRect(mLastRectangle, ovSize.x, ovSize.y); + break; + case PICT.OP_INVERT_R_RECT: + case PICT.OP_INVERT_SAME_R_RECT: + mContext.invertRoundRect(mLastRectangle, ovSize.x, ovSize.y); + break; + case PICT.OP_FILL_R_RECT: + case PICT.OP_FILL_SAME_R_RECT: + mContext.fillRoundRect(mLastRectangle, ovSize.x, ovSize.y, fill); + break; + } + + // Do verbose mode output + if (DEBUG) { + switch (opCode) { + case PICT.OP_FRAME_R_RECT: + System.out.println("frameRRect: " + mLastRectangle); + break; + case PICT.OP_PAINT_R_RECT: + System.out.println("paintRRect: " + mLastRectangle); + break; + case PICT.OP_ERASE_R_RECT: + System.out.println("eraseRRect: " + mLastRectangle); + break; + case PICT.OP_INVERT_R_RECT: + System.out.println("invertRRect: " + mLastRectangle); + break; + case PICT.OP_FILL_R_RECT: + System.out.println("fillRRect: " + mLastRectangle); + break; + case PICT.OP_FRAME_SAME_R_RECT: + System.out.println("frameSameRRect: " + mLastRectangle); + break; + case PICT.OP_PAINT_SAME_R_RECT: + System.out.println("paintSameRRect: " + mLastRectangle); + break; + case PICT.OP_ERASE_SAME_R_RECT: + System.out.println("eraseSameRRect: " + mLastRectangle); + break; + case PICT.OP_INVERT_SAME_R_RECT: + System.out.println("invertSameRRect: " + mLastRectangle); + break; + case PICT.OP_FILL_SAME_R_RECT: + System.out.println("fillSameRRect: " + mLastRectangle); + break; + } + } + + // RoundRect treatments finished + break; + + //-------------------------------------------------------------------------------- + // Oval treatments + //-------------------------------------------------------------------------------- + case PICT.OP_FRAME_OVAL:// OK + case PICT.OP_PAINT_OVAL:// OK, not tested + case PICT.OP_ERASE_OVAL:// OK, not tested + case PICT.OP_INVERT_OVAL:// OK, not tested + case PICT.OP_FILL_OVAL:// OK, not tested + // Get the frame rectangle + readRectangle(pStream, mLastRectangle); + case PICT.OP_FRAME_SAME_OVAL:// OK, not tested + case PICT.OP_PAINT_SAME_OVAL:// OK, not tested + case PICT.OP_ERASE_SAME_OVAL:// OK, not tested + case PICT.OP_INVERT_SAME_OVAL:// OK, not tested + case PICT.OP_FILL_SAME_OVAL:// OK, not tested + // Draw + switch (opCode) { + case PICT.OP_FRAME_OVAL: + case PICT.OP_FRAME_SAME_OVAL: + mContext.frameOval(mLastRectangle); + break; + case PICT.OP_PAINT_OVAL: + case PICT.OP_PAINT_SAME_OVAL: + mContext.paintOval(mLastRectangle); + break; + case PICT.OP_ERASE_OVAL: + case PICT.OP_ERASE_SAME_OVAL: + mContext.eraseOval(mLastRectangle); + break; + case PICT.OP_INVERT_OVAL: + case PICT.OP_INVERT_SAME_OVAL: + mContext.invertOval(mLastRectangle); + break; + case PICT.OP_FILL_OVAL: + case PICT.OP_FILL_SAME_OVAL: + mContext.fillOval(mLastRectangle, fill); + break; + } + + // Do verbose mode output + if (DEBUG) { + switch (opCode) { + case PICT.OP_FRAME_OVAL: + System.out.println("frameOval: " + mLastRectangle); + break; + case PICT.OP_PAINT_OVAL: + System.out.println("paintOval: " + mLastRectangle); + break; + case PICT.OP_ERASE_OVAL: + System.out.println("eraseOval: " + mLastRectangle); + break; + case PICT.OP_INVERT_OVAL: + System.out.println("invertOval: " + mLastRectangle); + break; + case PICT.OP_FILL_OVAL: + System.out.println("fillOval: " + mLastRectangle); + break; + case PICT.OP_FRAME_SAME_OVAL: + System.out.println("frameSameOval: " + mLastRectangle); + break; + case PICT.OP_PAINT_SAME_OVAL: + System.out.println("paintSameOval: " + mLastRectangle); + break; + case PICT.OP_ERASE_SAME_OVAL: + System.out.println("eraseSameOval: " + mLastRectangle); + break; + case PICT.OP_INVERT_SAME_OVAL: + System.out.println("invertSameOval: " + mLastRectangle); + break; + case PICT.OP_FILL_SAME_OVAL: + System.out.println("fillSameOval: " + mLastRectangle); + break; + } + } + + // Oval treatments finished + break; + + case 0x35: + case 0x36: + case 0x37: + case 0x45: + case 0x46: + case 0x47: + case 0x55: + case 0x56: + case 0x57: + pStream.readFully(new byte[8], 0, 8); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + //-------------------------------------------------------------------------------- + // Arc treatments + //-------------------------------------------------------------------------------- + case PICT.OP_FRAME_ARC:// OK, not tested + case PICT.OP_PAINT_ARC:// OK, not tested + case PICT.OP_ERASE_ARC:// OK, not tested + case PICT.OP_INVERT_ARC:// OK, not tested + case PICT.OP_FILL_ARC:// OK, not tested + // Get the frame rectangle + readRectangle(pStream, mLastRectangle); + case PICT.OP_FRAME_SAME_ARC:// OK, not tested + case PICT.OP_PAINT_SAME_ARC:// OK, not tested + case PICT.OP_ERASE_SAME_ARC:// OK, not tested + case PICT.OP_INVERT_SAME_ARC:// OK, not tested + case PICT.OP_FILL_SAME_ARC:// OK, not tested + // NOTE: These are inlcuded even if SAME + // Get start and end angles + //x = getXPtCoord(pStream.readUnsignedShort()); + //y = getYPtCoord(pStream.readUnsignedShort()); + x = pStream.readUnsignedShort(); + y = pStream.readUnsignedShort(); + arcAngles.setLocation(x, y); + + // Draw + switch (opCode) { + case PICT.OP_FRAME_ARC: + case PICT.OP_FRAME_SAME_ARC: + mContext.frameArc(mLastRectangle, arcAngles.x, arcAngles.y); + break; + case PICT.OP_PAINT_ARC: + case PICT.OP_PAINT_SAME_ARC: + mContext.paintArc(mLastRectangle, arcAngles.x, arcAngles.y); + break; + case PICT.OP_ERASE_ARC: + case PICT.OP_ERASE_SAME_ARC: + mContext.eraseArc(mLastRectangle, arcAngles.x, arcAngles.y); + break; + case PICT.OP_INVERT_ARC: + case PICT.OP_INVERT_SAME_ARC: + mContext.invertArc(mLastRectangle, arcAngles.x, arcAngles.y); + break; + case PICT.OP_FILL_ARC: + case PICT.OP_FILL_SAME_ARC: + mContext.fillArc(mLastRectangle, arcAngles.x, arcAngles.y, fill); + break; + } + + // Do verbose mode output + if (DEBUG) { + switch (opCode) { + case PICT.OP_FRAME_ARC: + System.out.println("frameArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_PAINT_ARC: + System.out.println("paintArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_ERASE_ARC: + System.out.println("eraseArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_INVERT_ARC: + System.out.println("invertArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_FILL_ARC: + System.out.println("fillArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_FRAME_SAME_ARC: + System.out.println("frameSameArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_PAINT_SAME_ARC: + System.out.println("paintSameArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_ERASE_SAME_ARC: + System.out.println("eraseSameArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_INVERT_SAME_ARC: + System.out.println("invertSameArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + case PICT.OP_FILL_SAME_ARC: + System.out.println("fillSameArc: " + mLastRectangle + ", angles:" + arcAngles); + break; + } + } + + // Arc treatments finished + break; + + case 0x65: + case 0x66: + case 0x67: + pStream.readFully(new byte[12], 0, 12); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + case 0x6d: + case 0x6e: + case 0x6f: + pStream.readFully(new byte[4], 0, 4); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + //-------------------------------------------------------------------------------- + // Polygon treatments + //-------------------------------------------------------------------------------- + case PICT.OP_FRAME_POLY:// OK + case PICT.OP_PAINT_POLY:// OK + case PICT.OP_ERASE_POLY:// OK, not tested + case PICT.OP_INVERT_POLY:// OK, not tested + case PICT.OP_FILL_POLY:// OK, not tested + // Read the polygon + polygon = readPoly(pStream, bounds); + + case PICT.OP_FRAME_SAME_POLY:// OK, not tested + case PICT.OP_PAINT_SAME_POLY:// OK, not tested + case PICT.OP_ERASE_SAME_POLY:// OK, not tested + case PICT.OP_INVERT_SAME_POLY:// OK, not tested + case PICT.OP_FILL_SAME_POLY:// OK, not tested + + // Draw + switch (opCode) { + case PICT.OP_FRAME_POLY: + case PICT.OP_FRAME_SAME_POLY: + mContext.framePoly(polygon); + break; + case PICT.OP_PAINT_POLY: + case PICT.OP_PAINT_SAME_POLY: + mContext.paintPoly(polygon); + break; + case PICT.OP_ERASE_POLY: + case PICT.OP_ERASE_SAME_POLY: + mContext.erasePoly(polygon); + break; + case PICT.OP_INVERT_POLY: + case PICT.OP_INVERT_SAME_POLY: + mContext.invertPoly(polygon); + break; + case PICT.OP_FILL_POLY: + case PICT.OP_FILL_SAME_POLY: + mContext.fillPoly(polygon, fill); + break; + } + + // Do verbose mode output + if (DEBUG) { + switch (opCode) { + case PICT.OP_FRAME_POLY: + verbosePolyCmd("framePoly", bounds, polygon); + break; + case PICT.OP_PAINT_POLY: + verbosePolyCmd("paintPoly", bounds, polygon); + break; + case PICT.OP_ERASE_POLY: + verbosePolyCmd("erasePoly", bounds, polygon); + break; + case PICT.OP_INVERT_POLY: + verbosePolyCmd("invertPoly", bounds, polygon); + break; + case PICT.OP_FILL_POLY: + verbosePolyCmd("fillPoly", bounds, polygon); + break; + case PICT.OP_FRAME_SAME_POLY: + verbosePolyCmd("frameSamePoly", bounds, polygon); + break; + case PICT.OP_PAINT_SAME_POLY: + verbosePolyCmd("paintSamePoly", bounds, polygon); + break; + case PICT.OP_ERASE_SAME_POLY: + verbosePolyCmd("eraseSamePoly", bounds, polygon); + break; + case PICT.OP_INVERT_SAME_POLY: + verbosePolyCmd("invertSamePoly", bounds, polygon); + break; + case PICT.OP_FILL_SAME_POLY: + verbosePolyCmd("fillSamePoly", bounds, polygon); + break; + } + } + + // Polygon treatments finished + break; + + case 0x75: + case 0x76: + case 0x77: + // Read the polygon + polygon = readPoly(pStream, bounds); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + //-------------------------------------------------------------------------------- + // Region treatments + //-------------------------------------------------------------------------------- + case PICT.OP_FRAME_RGN:// OK, not tested + case PICT.OP_PAINT_RGN:// OK, not tested + case PICT.OP_ERASE_RGN:// OK, not tested + case PICT.OP_INVERT_RGN:// OK, not tested + case PICT.OP_FILL_RGN:// OK, not tested + // Read the region + region = readRegion(pStream, bounds); + + case PICT.OP_FRAME_SAME_RGN:// OK, not tested + case PICT.OP_PAINT_SAME_RGN:// OK, not tested + case PICT.OP_ERASE_SAME_RGN:// OK, not tested + case PICT.OP_INVERT_SAME_RGN:// OK, not tested + case PICT.OP_FILL_SAME_RGN:// OK, not tested + // Draw + if (region != null && region.npoints > 1) { + switch (opCode) { + case PICT.OP_FRAME_RGN: + case PICT.OP_FRAME_SAME_RGN: + mContext.frameRegion(new Area(region)); + break; + case PICT.OP_PAINT_RGN: + case PICT.OP_PAINT_SAME_RGN: + mContext.paintRegion(new Area(region)); + break; + case PICT.OP_ERASE_RGN: + case PICT.OP_ERASE_SAME_RGN: + mContext.eraseRegion(new Area(region)); + break; + case PICT.OP_INVERT_RGN: + case PICT.OP_INVERT_SAME_RGN: + mContext.invertRegion(new Area(region)); + break; + case PICT.OP_FILL_RGN: + case PICT.OP_FILL_SAME_RGN: + mContext.fillRegion(new Area(region), fill); + break; + } + } + + // Do verbose mode output + if (DEBUG) { + switch (opCode) { + case PICT.OP_FRAME_RGN: + verboseRegionCmd("frameRgn", bounds, region); + break; + case PICT.OP_PAINT_RGN: + verboseRegionCmd("paintRgn", bounds, region); + break; + case PICT.OP_ERASE_RGN: + verboseRegionCmd("eraseRgn", bounds, region); + break; + case PICT.OP_INVERT_RGN: + verboseRegionCmd("invertRgn", bounds, region); + break; + case PICT.OP_FILL_RGN: + verboseRegionCmd("fillRgn", bounds, region); + break; + case PICT.OP_FRAME_SAME_RGN: + verboseRegionCmd("frameSameRgn", bounds, region); + break; + case PICT.OP_PAINT_SAME_RGN: + verboseRegionCmd("paintSameRgn", bounds, region); + break; + case PICT.OP_ERASE_SAME_RGN: + verboseRegionCmd("eraseSameRgn", bounds, region); + break; + case PICT.OP_INVERT_SAME_RGN: + verboseRegionCmd("invertSameRgn", bounds, region); + break; + case PICT.OP_FILL_SAME_RGN: + verboseRegionCmd("fillSameRgn", bounds, region); + break; + } + } + + // Region treatments finished + break; + + case 0x85: + case 0x86: + case 0x87: + // Read the region + region = readRegion(pStream, bounds); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + case PICT.OP_BITS_RECT: + // [4] Four opcodes ($0090, $0091, $0098, $0099) are modifications of version 1 opcodes. + // The first word following the opcode is rowBytes. If the high bit of rowBytes is set, + // then it is a pixel map containing multiple bits per pixel; if it is not set, it is a + // bitmap containing 1 bit per pixel. + // In general, the difference between version 2 and version 1 formats is that the pixel + // map replaces the bitmap, a color table has been added, and pixData replaces bitData. + // [5] For opcodes $0090 (BitsRect) and $0091 (BitsRgn), the data is unpacked. These + // opcodes can be used only when rowBytes is less than 8. + /* + PixMap: PixMap; {pixel map} + ColorTable: ColorTable; {ColorTable record} + srcRect: Rect; {source rectangle} + dstRect: Rect; {destination rectangle} + mode: Word; {transfer mode (may include } + { new transfer modes)} + PixData: PixData; + */ + + int rowBytesRaw = pStream.readUnsignedShort(); + int rowBytes = rowBytesRaw & 0x3FFF; + + // TODO: Use rowBytes to determine size of PixMap/ColorTable? + if ((rowBytesRaw & 0x8000) > 0) { + // Do stuff... + } + + // Get bounds rectangle. THIS IS NOT TO BE SCALED BY THE RESOLUTION! TODO: ?! + bounds = new Rectangle(); + y = pStream.readUnsignedShort(); + x = pStream.readUnsignedShort(); + bounds.setLocation(x, y); + + y = pStream.readUnsignedShort(); + x = pStream.readUnsignedShort(); + bounds.setSize(x - bounds.x, + y - bounds.y); + + Rectangle srcRect = new Rectangle(); + readRectangle(pStream, srcRect); + + Rectangle dstRect = new Rectangle(); + readRectangle(pStream, dstRect); + + mode = pStream.readUnsignedShort(); + mContext.setPenMode(mode); // TODO: Or parameter? + + if (DEBUG) { + System.out.print("bitsRect, rowBytes: " + rowBytes); + if ((rowBytesRaw & 0x8000) > 0) { + System.out.print(", it is a PixMap"); + } + else { + System.out.print(", it is a BitMap"); + } + System.out.print(", bounds: " + bounds); + System.out.print(", srcRect: " + srcRect); + System.out.print(", dstRect: " + dstRect); + System.out.print(", mode: " + mode); + System.out.println(); + } + + BufferedImage image = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_BYTE_BINARY); + byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); + + // Read pixel data + int width = bounds.width / 8; + for (int i = 0; i < bounds.height; i++) { + pStream.readFully(data, i * width, width); + pStream.skipBytes(rowBytes - width); + } + + // Draw pixel data + Rectangle rect = new Rectangle(srcRect); + rect.translate(-bounds.x, -bounds.y); + mContext.copyBits(image, rect, dstRect, mode, null); + //mGraphics.drawImage(image, + // dstRect.x, dstRect.y, + // dstRect.x + dstRect.width, dstRect.y + dstRect.height, + // srcRect.x - bounds.x, srcRect.y - bounds.y, + // srcRect.x - bounds.x + srcRect.width, srcRect.y - bounds.y + srcRect.height, + // null); + // + break; + + case PICT.OP_BITS_RGN: + // TODO: As OP_BITS_RECT but with clip + /* + pixMap: PixMap; + colorTable: ColorTable; + srcRect: Rect; {source rectangle} + dstRect: Rect; {destination rectangle} + mode: Word; {transfer mode (may } + { include new modes)} + maskRgn: Rgn; {region for masking} + pixData: PixData; + */ + if (DEBUG) { + System.out.println("bitsRgn"); + } + break; + + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + dataLength = pStream.readUnsignedShort(); + pStream.readFully(new byte[dataLength], 0, dataLength); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + case PICT.OP_PACK_BITS_RECT: + readOpPackBitsRect(pStream, bounds, pixmapCount++); + if (DEBUG) { + System.out.println("packBitsRect - TODO"); + } + break; + + case PICT.OP_PACK_BITS_RGN: + // TODO: As OP_PACK_BITS_RECT but with clip + // TODO: Read/Skip data + if (DEBUG) { + System.out.println("packBitsRgn - TODO"); + } + break; + + case PICT.OP_DIRECT_BITS_RECT: + readOpDirectBitsRect(pStream, bounds, pixmapCount++); + break; + + case PICT.OP_DIRECT_BITS_RGN: + // TODO: As OP_DIRECT_BITS_RECT but with clip + // TODO: Read/Skip data + if (DEBUG) { + System.out.println("directBitsRgn - TODO"); + } + break; + + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + // TODO: Move to special Apple Reserved handling? + dataLength = pStream.readUnsignedShort(); + pStream.readFully(new byte[dataLength], 0, dataLength); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); + } + break; + + case PICT.OP_SHORT_COMMENT:// NOTHING TO DO, JUST JUMP OVER + pStream.readFully(new byte[2], 0, 2); + if (DEBUG) { + System.out.println("Short comment"); + } + break; + + case PICT.OP_LONG_COMMENT:// NOTHING TO DO, JUST JUMP OVER + readLongComment(pStream); + if (DEBUG) { + System.out.println("Long comment"); + } + break; + + case PICT.OP_END_OF_PICTURE:// OK + break; + + // WE DON'T CARE ABOUT CODES 0x100 to 0x2FE, even if it might be needed + + // WE DON'T CARE ABOUT CODES 0x300 to 0xBFF, even if it might be needed + + // WE DON'T CARE ABOUT CODES 0xC01 to 0x81FF, even if it might be needed + + case PICT.OP_COMPRESSED_QUICKTIME: + // $8200: CompressedQuickTime Data length (Long), data (private to QuickTime) 4 + data length + if (DEBUG) { + System.out.println("compressedQuickTime"); + } + + readCompressedQT(pStream); + + break; + + case PICT.OP_UNCOMPRESSED_QUICKTIME:// JUST JUMP OVER + // $8201: UncompressedQuickTime Data length (Long), data (private to QuickTime) 4 + data length + // TODO: Read this as well, need test data + dataLength = pStream.readInt(); + pStream.readFully(new byte[dataLength], 0, dataLength); + if (DEBUG) { + System.out.println("unCompressedQuickTime"); + } + break; + + case 0xFFFF:// JUST JUMP OVER + dataLength = pStream.readInt(); + pStream.readFully(new byte[dataLength], 0, dataLength); + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x - length: %s", PICT.APPLE_USE_RESERVED_FIELD, opCode, dataLength)); + } + break; + + default: + // See: http://developer.apple.com/DOCUMENTATION/mac/QuickDraw/QuickDraw-461.html + if (opCode >= 0x00a0 && opCode <= 0x00af) { + dataLength = pStream.readUnsignedShort(); + pStream.readFully(new byte[dataLength], 0, dataLength); + } + else if (opCode >= 0x00b0 && opCode <= 0x00cf) { + // Zero-length + dataLength = 0; + } + else if (opCode >= 0x00d0 && opCode <= 0x00fe) { + dataLength = pStream.readInt(); + pStream.readFully(new byte[dataLength], 0, dataLength); + } + else if (opCode >= 0x0100 && opCode <= 0x7fff) { + // For opcodes $0100-$7FFF, the amount of data for + // opcode $nnXX = 2 times nn bytes. + dataLength = ((opCode & 0xff00) >> 8) * 2; + pStream.skipBytes(dataLength); + } + else if (opCode >= 0x8000 && opCode <= 0x80ff) { + // Zero-length + dataLength = 0; + } + else if (opCode >= 0x8100 && opCode <= 0x81ff) { + dataLength = pStream.readInt(); + pStream.readFully(new byte[dataLength], 0, dataLength); + } + else { + throw new IIOException(String.format("Found unknown opcode: 0x%04x", opCode)); + } + + if (DEBUG) { + System.out.println(String.format("%s: 0x%04x - length: %s", PICT.APPLE_USE_RESERVED_FIELD, opCode, dataLength)); + } + } + } + while (opCode != PICT.OP_END_OF_PICTURE); + } + catch (IIOException e) { + throw e; + } + catch (EOFException e) { + throw new IIOException("Error in PICT format: Unexpected end of File", e); + } + catch (IOException e) { + throw new IIOException("Error in PICT format: " + e.getMessage(), e); + } + } + + /* + http://devworld.apple.com/documentation/QuickTime/RM/CompressDecompress/ImageComprMgr/B-Chapter/chapter_1000_section_5.html: + + Field name Description Data size (in bytes) + + Opcode Compressed picture data 2 + Size Size in bytes of data for this opcode 4 + Version Version of this opcode 2 + Matrix 3 by 3 fixed transformation matrix 36 + MatteSize Size of matte data in bytes 4 + MatteRect Rectangle for matte data 8 + Mode Transfer mode 2 + SrcRect Rectangle for source 8 + Accuracy Preferred accuracy 4 + MaskSize Size of mask region in bytes 4 + */ + private void readCompressedQT(final ImageInputStream pStream) throws IOException { + int dataLength = pStream.readInt(); + long pos = pStream.getStreamPosition(); + + if (DEBUG) { + System.out.println("QT data length: " + dataLength); + } + + // TODO: Need to figure out what the skipped data is? + for (int i = 0; i < 13; i++) { + int value = pStream.readInt(); + if (DEBUG) { + System.out.println(String.format("%2d: 0x%08x", i * 4, value)); + } + } + + // Read the destination rectangle + Rectangle destination = new Rectangle(); + readRectangle(pStream, destination); + + if (DEBUG) { + System.out.println("..."); + } + + for (int i = 0; i < 2; i++) { + int value = pStream.readInt(); + if (DEBUG) { + System.out.println(String.format("%2d: 0x%08x", (i + 15) * 4, value)); + } + } + + BufferedImage image = QuickTime.decompress(pStream); + + if (image != null) { + mContext.copyBits(image, new Rectangle(image.getWidth(), image.getHeight()), destination, QuickDraw.SRC_COPY, null); + + pStream.seek(pos + dataLength); // Might be word-align mismatch here + + // Skip "QuickTime? and a ... decompressor required" text + // TODO: Verify that this is correct. It works with all my test data, but the algorithm is + // reverse-engineered by looking at the input data and not from any spec I've seen... + int penSizeMagic = pStream.readInt(); + if (penSizeMagic == 0x000700ae) { // OP_PN_SIZE + bogus x value..? + int skip = pStream.readUnsignedShort(); // bogus y value is the number of bytes to skip + pStream.skipBytes(skip); // Following opcode should be a OP_PN_SIZE with real values + } + else { + pStream.seek(pos + dataLength); + } + } + else { + pStream.seek(pos + dataLength); + } + } + + /* + http://devworld.apple.com/documentation/QuickTime/RM/CompressDecompress/ImageComprMgr/B-Chapter/chapter_1000_section_5.html: + + Field name Description Data size (in bytes) + + Opcode Uncompressed picture data 2 + Size Size in bytes of data for this opcode 4 + Version Version of this opcode 2 + Matrix 3 by 3 fixed transformation matrix 36 + MatteSize Size of matte data in bytes 4 + MatteRect Rectangle for matte data 8 + */ + + + private void readOpPackBitsRect(ImageInputStream pStream, Rectangle pBounds, int pPixmapCount) throws IOException { + if (DEBUG) { + System.out.println("packBitsRect"); + } + + // Skip PixMap pointer (always 0x000000FF); +// pStream.skipBytes(4); +// int pixmapPointer = pStream.readInt(); +// System.out.println(String.format("%08d: 0x%08x", pStream.getStreamPosition(), pixmapPointer)); + + // Get rowBytes + int rowBytesRaw = pStream.readUnsignedShort(); +// System.out.println(String.format("%08d: 0x%04x", pStream.getStreamPosition(), rowBytesRaw)); + int rowBytes = rowBytesRaw & 0x3FFF; + if (DEBUG) { + System.out.print("packBitsRect, rowBytes: " + rowBytes); + if ((rowBytesRaw & 0x8000) > 0) { + System.out.print(", it is a PixMap"); + } + else { + System.out.print(", it is a BitMap"); + } + } + + // Get bounds rectangle. THIS IS NOT TO BE SCALED BY THE RESOLUTION! + int y = pStream.readUnsignedShort(); + int x = pStream.readUnsignedShort(); + pBounds.setLocation(x, y); + + y = pStream.readUnsignedShort(); + x = pStream.readUnsignedShort(); + pBounds.setSize(x - pBounds.x, y - pBounds.y); + if (DEBUG) { + System.out.print(", bounds: " + pBounds); + } + + // Get PixMap record version number + int pmVersion = pStream.readUnsignedShort() & 0xFFFF; + if (DEBUG) { + System.out.print(", pmVersion: " + pmVersion); + } + + // Get packing format + int packType = pStream.readUnsignedShort() & 0xFFFF; + if (DEBUG) { + System.out.print(", packType: " + packType); + } + + // Get size of packed data (not used for v2) + int packSize = pStream.readInt(); + if (DEBUG) { + System.out.println(", packSize: " + packSize); + } + + // Get resolution info + double hRes = PICTUtil.readFixedPoint(pStream); + double vRes = PICTUtil.readFixedPoint(pStream); + if (DEBUG) { + System.out.print("hRes: " + hRes + ", vRes: " + vRes); + } + + // Get pixel type + int pixelType = pStream.readUnsignedShort(); + if (DEBUG) { + if (pixelType == 0) { + System.out.print(", indexed pixels"); + } + else { + System.out.print(", RGBDirect"); + } + } + + // Get pixel size + int pixelSize = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.print(", pixelSize:" + pixelSize); + } + + // Get pixel component count + int cmpCount = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.print(", cmpCount:" + cmpCount); + } + + // Get pixel component size + int cmpSize = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.print(", cmpSize:" + cmpSize); + } + + // planeBytes (ignored) + int planeBytes = pStream.readInt(); + if (DEBUG) { + System.out.print(", planeBytes:" + planeBytes); + } + + // Handle to ColorTable record, there should be none for direct + // bits so this should be 0, just skip + int clutId = pStream.readInt(); + if (DEBUG) { + System.out.println(", clutId:" + clutId); + } + + // Reserved + pStream.readInt(); + + // Color table + ColorModel colorModel; + if (pixelType == 0) { + colorModel = PICTUtil.readColorTable(pStream, pixelSize); + } + else { + throw new IIOException("Unsupported pixel type: " + pixelType); + } + + // Get source rectangle. We DO NOT scale the coordinates by the + // resolution info, since we are in pixmap coordinates here + Rectangle srcRect = new Rectangle(); + y = pStream.readUnsignedShort(); + x = pStream.readUnsignedShort(); + srcRect.setLocation(x, y); + + y = pStream.readUnsignedShort(); + x = pStream.readUnsignedShort(); + srcRect.setSize(x - srcRect.x, y - srcRect.y); + + if (DEBUG) { + System.out.print("opPackBitsRect, srcRect:" + srcRect); + } + + // TODO: FixMe... + // Get destination rectangle. We DO scale the coordinates according to + // the image resolution, since we are working in display coordinates + Rectangle dstRect = new Rectangle(); + readRectangle(pStream, dstRect); + if (DEBUG) { + System.out.print(", dstRect:" + dstRect); + } + + // Get transfer mode + int transferMode = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.print(", mode: " + transferMode); + } + + // Set up pixel buffer for the RGB values + + // TODO: Seems to be packType 0 all the time? + // packType = 0 means default.... + + + // Read in the RGB arrays + byte[] dstBytes; + /* + if (packType == 1 || rowBytes < 8) { + // TODO: Verify this... + dstBytes = new byte[rowBytes]; + } + else if (packType == 2) { + // TODO: Verify this... + dstBytes = new byte[rowBytes * 3 / 4]; + } + else if (packType == 3) { + dstBytes = new byte[2 * pBounds.width]; + } + else if (packType == 4) { + dstBytes = new byte[cmpCount * pBounds.width]; + } + else { + throw new IIOException("Unknown pack type: " + packType); + } + */ + if (packType == 0) { + dstBytes = new byte[cmpCount * pBounds.width]; + } + else { + throw new IIOException("Unknown pack type: " + packType); + } + +// int[] pixArray = new int[pBounds.height * pBounds.width]; + byte[] pixArray = new byte[pBounds.height * pBounds.width]; + int pixBufOffset = 0; + + int packedBytesCount; + for (int scanline = 0; scanline < pBounds.height; scanline++) { + // Get byteCount of the scanline + if (rowBytes > 250) { + packedBytesCount = pStream.readUnsignedShort(); + } + else { + packedBytesCount = pStream.readUnsignedByte(); + } + if (DEBUG) { + System.out.println(); + System.out.print("Line " + scanline + ", byteCount: " + packedBytesCount); + System.out.print(" dstBytes: " + dstBytes.length); + } + + // Read in the scanline + /*if (packType > 2) { + // Unpack them all*/ + Decoder decoder;/* + if (packType == 3) { + decoder = new PackBits16Decoder(); + } + else {*/ + decoder = new PackBitsDecoder(); + /*}*/ + DataInput unPackBits = new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pStream, packedBytesCount), decoder)); +// unPackBits.readFully(dstBytes); + unPackBits.readFully(pixArray, pixBufOffset, pBounds.width); + /*} + else { + mImageInput.readFully(dstBytes); + }*/ + + // TODO: Use TYPE_USHORT_555_RGB for 16 bit + /* + if (packType == 3) { + for (int i = 0; i < pBounds.width; i++) { + // Set alpha values to all opaque + pixArray[pixBufOffset + i] = 0xFF000000; + + // Get red values + int red = 8 * ((dstBytes[2 * i] & 0x7C) >> 2); + pixArray[pixBufOffset + i] |= red << 16; + // Get green values + int green = 8 * (((dstBytes[2 * i] & 0x07) << 3) + ((dstBytes[2 * i + 1] & 0xE0) >> 5)); + pixArray[pixBufOffset + i] |= green << 8; + // Get blue values + int blue = 8 * ((dstBytes[2 * i + 1] & 0x1F)); + pixArray[pixBufOffset + i] |= blue; + } + } + else { + if (cmpCount == 3) { + for (int i = 0; i < pBounds.width; i++) { + // Set alpha values to all opaque + pixArray[pixBufOffset + i] = 0xFF000000; + // Get red values + pixArray[pixBufOffset + i] |= (dstBytes[i] & 0xFF) << 16; + // Get green values + pixArray[pixBufOffset + i] |= (dstBytes[pBounds.width + i] & 0xFF) << 8; + // Get blue values + pixArray[pixBufOffset + i] |= (dstBytes[2 * pBounds.width + i] & 0xFF); + } + } + else { + for (int i = 0; i < pBounds.width; i++) { +// // Get alpha values +// pixArray[pixBufOffset + i] = (dstBytes[i] & 0xFF) << 24; +// // Get red values +// pixArray[pixBufOffset + i] |= (dstBytes[pBounds.width + i] & 0xFF) << 16; +// // Get green values +// pixArray[pixBufOffset + i] |= (dstBytes[2 * pBounds.width + i] & 0xFF) << 8; +// // Get blue values +// pixArray[pixBufOffset + i] |= (dstBytes[3 * pBounds.width + i] & 0xFF); + + // TODO: Fake it for now... Should ideally just use byte array and use the ICM +// pixArray[pixBufOffset + i] = 0xFF << 24; +// pixArray[pixBufOffset + i] |= colorModel.getRed(dstBytes[i] & 0xFF) << 16; +// pixArray[pixBufOffset + i] |= colorModel.getGreen(dstBytes[i] & 0xFF) << 8; +// pixArray[pixBufOffset + i] |= colorModel.getBlue(dstBytes[i] & 0xFF); + + pixArray[pixBufOffset + i] = dstBytes[i]; + } +// } +// } +*/ + + // Increment pixel buffer offset + pixBufOffset += pBounds.width; + + //////////////////////////////////////////////////// + // TODO: This works for single image PICTs only... + // However, this is the most common case. Ok for now + processImageProgress(scanline * 100 / pBounds.height); + if (abortRequested()) { + processReadAborted(); + + // Skip rest of image data + for (int skip = scanline + 1; skip < pBounds.height; skip++) { + // Get byteCount of the scanline + if (rowBytes > 250) { + packedBytesCount = pStream.readUnsignedShort(); + } + else { + packedBytesCount = pStream.readUnsignedByte(); + } + pStream.readFully(new byte[packedBytesCount], 0, packedBytesCount); + + if (DEBUG) { + System.out.println(); + System.out.print("Skip " + skip + ", byteCount: " + packedBytesCount); + } + } + + break; + } + //////////////////////////////////////////////////// + } + + // We add all new images to it. If we are just replaying, then + // "pPixmapCount" will never be greater than the size of the vector + if (mImages.size() <= pPixmapCount) { + // Create BufferedImage and add buffer it for multiple reads +// DirectColorModel cm = (DirectColorModel) ColorModel.getRGBdefault(); +// DataBuffer db = new DataBufferInt(pixArray, pixArray.length); +// WritableRaster raster = Raster.createPackedRaster(db, pBounds.width, pBounds.height, pBounds.width, cm.getMasks(), null); + DataBuffer db = new DataBufferByte(pixArray, pixArray.length); + WritableRaster raster = Raster.createPackedRaster(db, pBounds.width, pBounds.height, cmpSize, null); // TODO: last param should ideally be srcRect.getLocation() + BufferedImage img = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null); + + mImages.add(img); + } + + // Draw the image + BufferedImage img = mImages.get(pPixmapCount); + if (img != null) { + // TODO: FixMe.. Seems impossible to create a bufferedImage with a raster not starting at 0,0 + srcRect.setLocation(0, 0); // should not require this line.. + mContext.copyBits(img, srcRect, dstRect, transferMode, null); + } + + // Line break at the end + if (DEBUG) { + System.out.println(); + } + } + + /** + * Reads the data following a {@code directBitsRect} opcode. + * + * @param pStream the stream to read from + * @param pBounds the bounding rectangle + * @param pPixmapCount the index of the bitmap in the PICT file, used for + * cahcing. + * + * @throws javax.imageio.IIOException if the data can not be read. + * @throws IOException if an I/O error occurs while reading the image. + */ + private void readOpDirectBitsRect(ImageInputStream pStream, Rectangle pBounds, int pPixmapCount) throws IOException { + if (DEBUG) { + System.out.println("directBitsRect"); + } + + // Skip PixMap pointer (always 0x000000FF); + pStream.skipBytes(4); + + // Get rowBytes + int rowBytesRaw = pStream.readUnsignedShort(); + int rowBytes = rowBytesRaw & 0x3FFF; + if (DEBUG) { + System.out.print("directBitsRect, rowBytes: " + rowBytes); + if ((rowBytesRaw & 0x8000) > 0) { + System.out.print(", it is a PixMap"); + } + else { + System.out.print(", it is a BitMap"); + } + } + + // Get bounds rectangle. THIS IS NOT TO BE SCALED BY THE RESOLUTION! + int y = pStream.readUnsignedShort(); + int x = pStream.readUnsignedShort(); + pBounds.setLocation(x, y); + + y = pStream.readUnsignedShort(); + x = pStream.readUnsignedShort(); + pBounds.setSize(x - pBounds.x, y - pBounds.y); + if (DEBUG) { + System.out.print(", bounds: " + pBounds); + } + + // Get PixMap record version number + int pmVersion = pStream.readUnsignedShort() & 0xFFFF; + if (DEBUG) { + System.out.print(", pmVersion: " + pmVersion); + } + + // Get packing format + int packType = pStream.readUnsignedShort() & 0xFFFF; + if (DEBUG) { + System.out.print(", packType: " + packType); + } + + // Get size of packed data (not used for v2) + int packSize = pStream.readInt(); + if (DEBUG) { + System.out.println(", packSize: " + packSize); + } + + // Get resolution info + double hRes = PICTUtil.readFixedPoint(pStream); + double vRes = PICTUtil.readFixedPoint(pStream); + if (DEBUG) { + System.out.print("hRes: " + hRes + ", vRes: " + vRes); + } + + // Get pixel type + int pixelType = pStream.readUnsignedShort(); + if (DEBUG) { + if (pixelType == 0) { + System.out.print(", indexed pixels"); + } + else { + System.out.print(", RGBDirect"); + } + } + + // Get pixel size + int pixelSize = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.print(", pixelSize:" + pixelSize); + } + + // Get pixel component count + int cmpCount = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.print(", cmpCount:" + cmpCount); + } + + // Get pixel component size + int cmpSize = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.println(", cmpSize:" + cmpSize); + } + + // planeBytes (ignored) + pStream.readInt(); + + // Handle to ColorTable record, there should be none for direct + // bits so this should be 0, just skip + pStream.readInt(); + + // Reserved + pStream.readInt(); + + // Get source rectangle. We DO NOT scale the coordinates by the + // resolution info, since we are in pixmap coordinates here + Rectangle srcRect = new Rectangle(); + y = pStream.readUnsignedShort(); + x = pStream.readUnsignedShort(); + srcRect.setLocation(x, y); + + y = pStream.readUnsignedShort(); + x = pStream.readUnsignedShort(); + srcRect.setSize(x - srcRect.x, y - srcRect.y); + + if (DEBUG) { + System.out.print("opDirectBitsRect, srcRect:" + srcRect); + } + + // TODO: FixMe... + // Get destination rectangle. We DO scale the coordinates according to + // the image resolution, since we are working in display coordinates + Rectangle dstRect = new Rectangle(); + readRectangle(pStream, dstRect); + if (DEBUG) { + System.out.print(", dstRect:" + dstRect); + } + + // Get transfer mode + int transferMode = pStream.readUnsignedShort(); + if (DEBUG) { + System.out.print(", mode: " + transferMode); + } + + // Set up pixel buffer for the RGB values + + // Read in the RGB arrays + byte[] dstBytes; + if (packType == 1 || rowBytes < 8) { + // TODO: Verify this... + dstBytes = new byte[rowBytes]; + } + else if (packType == 2) { + // TODO: Verify this... + dstBytes = new byte[rowBytes * 3 / 4]; + } + else if (packType == 3) { + dstBytes = new byte[2 * pBounds.width]; + } + else if (packType == 4) { + dstBytes = new byte[cmpCount * pBounds.width]; + } + else { + throw new IIOException("Unknown pack type: " + packType); + } + + int[] pixArray = null; + short[] shortArray = null; + if (packType == 3) { + shortArray = new short[pBounds.height * pBounds.width]; + } + else { + pixArray = new int[pBounds.height * pBounds.width]; + } + + int pixBufOffset = 0; + + int packedBytesCount; + for (int scanline = 0; scanline < pBounds.height; scanline++) { + // Get byteCount of the scanline + if (rowBytes > 250) { + packedBytesCount = pStream.readUnsignedShort(); + } + else { + packedBytesCount = pStream.readUnsignedByte(); + } + if (DEBUG) { + System.out.println(); + System.out.print("Line " + scanline + ", byteCount: " + packedBytesCount); + System.out.print(" dstBytes: " + dstBytes.length); + } + + // Read in the scanline + if (packType > 2) { + // Unpack them all + Decoder decoder; + if (packType == 3) { + decoder = new PackBits16Decoder(); + } + else { + decoder = new PackBitsDecoder(); + } + DataInput unPackBits = new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pStream, packedBytesCount), decoder)); + unPackBits.readFully(dstBytes); + } + else { + mImageInput.readFully(dstBytes); + } + + if (packType == 3) { + // TYPE_USHORT_555_RGB for 16 bit + for (int i = 0; i < pBounds.width; i++) { + shortArray[pixBufOffset + i] = (short) (((0xff & dstBytes[2 * i]) << 8) | (0xff & dstBytes[2 * i + 1])); +// // Set alpha values to all opaque +// pixArray[pixBufOffset + i] = 0xFF000000; +// +// // Get red values +// int red = 8 * ((dstBytes[2 * i] & 0x7C) >> 2); +// pixArray[pixBufOffset + i] |= red << 16; +// // Get green values +// int green = 8 * (((dstBytes[2 * i] & 0x07) << 3) + ((dstBytes[2 * i + 1] & 0xE0) >> 5)); +// pixArray[pixBufOffset + i] |= green << 8; +// // Get blue values +// int blue = 8 * ((dstBytes[2 * i + 1] & 0x1F)); +// pixArray[pixBufOffset + i] |= blue; + } + } + else { + if (cmpCount == 3) { + // RGB + for (int i = 0; i < pBounds.width; i++) { + // Set alpha values to all opaque + pixArray[pixBufOffset + i] = 0xFF000000; + // Get red values + pixArray[pixBufOffset + i] |= (dstBytes[i] & 0xFF) << 16; + // Get green values + pixArray[pixBufOffset + i] |= (dstBytes[pBounds.width + i] & 0xFF) << 8; + // Get blue values + pixArray[pixBufOffset + i] |= (dstBytes[2 * pBounds.width + i] & 0xFF); + } + } + else { + // ARGB + for (int i = 0; i < pBounds.width; i++) { + // Get alpha values + pixArray[pixBufOffset + i] = (dstBytes[i] & 0xFF) << 24; + // Get red values + pixArray[pixBufOffset + i] |= (dstBytes[pBounds.width + i] & 0xFF) << 16; + // Get green values + pixArray[pixBufOffset + i] |= (dstBytes[2 * pBounds.width + i] & 0xFF) << 8; + // Get blue values + pixArray[pixBufOffset + i] |= (dstBytes[3 * pBounds.width + i] & 0xFF); + } + } + } + + // Increment pixel buffer offset + pixBufOffset += pBounds.width; + + //////////////////////////////////////////////////// + // TODO: This works for single image PICTs only... + // However, this is the most common case. Ok for now + processImageProgress(scanline * 100 / pBounds.height); + if (abortRequested()) { + processReadAborted(); + + // Skip rest of image data + for (int skip = scanline + 1; skip < pBounds.height; skip++) { + // Get byteCount of the scanline + if (rowBytes > 250) { + packedBytesCount = pStream.readUnsignedShort(); + } + else { + packedBytesCount = pStream.readUnsignedByte(); + } + pStream.readFully(new byte[packedBytesCount], 0, packedBytesCount); + + if (DEBUG) { + System.out.println(); + System.out.print("Skip " + skip + ", byteCount: " + packedBytesCount); + } + } + + break; + } + //////////////////////////////////////////////////// + } + + // We add all new images to it. If we are just replaying, then + // "pPixmapCount" will never be greater than the size of the vector + if (mImages.size() <= pPixmapCount) { + // Create BufferedImage and add buffer it for multiple reads + DirectColorModel cm; + WritableRaster raster; + + if (packType == 3) { + cm = new DirectColorModel(15, 0x7C00, 0x03E0, 0x001F); // See BufferedImage TYPE_USHORT_555_RGB + DataBuffer db = new DataBufferUShort(shortArray, shortArray.length); + raster = Raster.createPackedRaster(db, pBounds.width, pBounds.height, pBounds.width, cm.getMasks(), null); // TODO: last param should ideally be srcRect.getLocation() + } + else { + cm = (DirectColorModel) ColorModel.getRGBdefault(); + DataBuffer db = new DataBufferInt(pixArray, pixArray.length); + raster = Raster.createPackedRaster(db, pBounds.width, pBounds.height, pBounds.width, cm.getMasks(), null); // TODO: last param should ideally be srcRect.getLocation() + } + + BufferedImage img = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null); + + mImages.add(img); + } + + // Draw the image + BufferedImage img = mImages.get(pPixmapCount); + if (img != null) { + // TODO: FixMe.. Something wrong here, might be the copyBits methods. + srcRect.setLocation(0, 0); // should not require this line.. + mContext.copyBits(img, srcRect, dstRect, transferMode, null); + } + + // Line break at the end + if (DEBUG) { + System.out.println(); + } + + } + + /** + * Reads the rectangle location and size from an 8-byte rectangle stream. + * + * @param pStream the stream to read from + * @param pDestRect the rectangle to read into + * + * @throws NullPointerException if {@code pDestRect} is {@code null} + * @throws IOException if an I/O error occurs while reading the image. + */ + private void readRectangle(DataInput pStream, Rectangle pDestRect) throws IOException { + int y = pStream.readUnsignedShort(); + int x = pStream.readUnsignedShort(); + int h = pStream.readUnsignedShort(); + int w = pStream.readUnsignedShort(); + + pDestRect.setLocation(getXPtCoord(x), getYPtCoord(y)); + pDestRect.setSize(getXPtCoord(w - x), getYPtCoord(h - y)); + + } + + /** + * Read in a region. The inputstream should be positioned at the first byte + * of the region. {@code pBoundsRect} is a rectangle that will be set to the + * region bounds. + * The point array may therefore be empty if the region is just a rectangle. + * + * @param pStream the stream to read from + * @param pBoundsRect the bounds rectangle to read into + * + * @return the polygon containing the region, or an empty polygon if the + * region is a rectanlge. + * + * @throws IOException if an I/O error occurs while reading the image. + */ + private Polygon readRegion(DataInput pStream, Rectangle pBoundsRect) throws IOException { + // Get minimal region + + // Get region data size + int size = pStream.readUnsignedShort(); + + // Get region bounds + int y = getYPtCoord(pStream.readUnsignedShort()); + int x = getXPtCoord(pStream.readUnsignedShort()); + pBoundsRect.setLocation(x, y); + + y = getYPtCoord(pStream.readShort()) - pBoundsRect.getLocation().y; + x = getXPtCoord(pStream.readShort()) - pBoundsRect.getLocation().x; + pBoundsRect.setSize(x, y); + + // Initialize the point array to the right size + int points = (size - 10) / (2 * 2); + + // Get the rest of the polygon points + Polygon polygon = new Polygon(); + for (int i = 0; i < points; i++) { + x = getXPtCoord(pStream.readShort()); + y = getYPtCoord(pStream.readShort()); + + polygon.addPoint(x, y); + } + + return polygon; + } + + /* + * Read in a polygon. The inputstream should be positioned at the first byte + * of the polygon. + */ + private Polygon readPoly(DataInput pStream, Rectangle pBoundsRect) throws IOException { + // Get polygon data size + int size = pStream.readUnsignedShort(); + + // Get poly bounds + int y = getYPtCoord(pStream.readShort()); + int x = getXPtCoord(pStream.readShort()); + pBoundsRect.setLocation(x, y); + + y = getYPtCoord(pStream.readShort()) - pBoundsRect.getLocation().y; + x = getXPtCoord(pStream.readShort()) - pBoundsRect.getLocation().x; + pBoundsRect.setSize(x, y); + + // Initialize the point array to the right size + int points = (size - 10) / (2 * 2); + + // Get the rest of the polygon points + Polygon polygon = new Polygon(); + + for (int i = 0; i < points; i++) { + y = getYPtCoord(pStream.readShort()); + x = getXPtCoord(pStream.readShort()); + polygon.addPoint(x, y); + } + + return polygon; + } + + // TODO: Support color pixel patterns! + /* + * Read PixPat. Read a PixPat data structure from the stream. Just returns + * void for the moment since not used in AWT graphics. NOT IMPLEMENTED YET! + */ + /* + private void readPixPat(DataInput pStream) { + byte[] count = new byte[1]; + byte[] text_bytes; + + try { + // Comment kind and data byte count + pStream.readFully(count, 0, count.length); + + // Get as many bytes as indicated by byte count + int text_byte_count = count[0]; + text_bytes = new byte[text_byte_count]; + pStream.readFully(text_bytes, 0, text_byte_count); + } catch ( IOException e ) { return null; } + return new String(text_bytes); + } + */ + + /* + * Read a long comment from the stream. + */ + private void readLongComment(DataInput pStream) throws IOException { + // Comment kind and data byte count + pStream.readShort(); + + // Get as many bytes as indicated by byte count + int length = pStream.readUnsignedShort(); + pStream.readFully(new byte[length], 0, length); + } + + /* + * Return the X coordinate value in display coordinates for the given + * coordinate value. This means multiplying it with the screen resolution/ + * image resolution ratio. + */ + private int getXPtCoord(int pPoint) { + return (int) (pPoint / mScreenImageXRatio); + } + + /* + * Return the Y coordinate value in display coordinates for the given + * coordinate value. This means multiplying it with the screen resolution/ + * image resolution ratio. + */ + private int getYPtCoord(int pPoint) { + return (int) (pPoint / mScreenImageYRatio); + } + + /* + * Write out polygon command, bounds and points. + */ + private void verbosePolyCmd(String pCmd, Rectangle pBounds, Polygon pPolygon) { + int i; + + System.out.println(pCmd + ": " + new Rectangle(pBounds.x, pBounds.y, pBounds.width, pBounds.height)); + System.out.print("Polygon points: "); + for (i = 0; pPolygon != null && i < pPolygon.npoints - 1; i++) { + System.out.print("(" + pPolygon.xpoints[i] + "," + pPolygon.ypoints[i] + "), "); + } + if (pPolygon != null && pPolygon.npoints > 0) { + System.out.print("(" + pPolygon.xpoints[i] + "," + pPolygon.ypoints[i] + ")"); + } + System.out.println(); + } + + /* + * Write out region command, bounds and points. + */ + private void verboseRegionCmd(String pCmd, Rectangle pBounds, Polygon pPolygon) { + int i; + + System.out.println(pCmd + ": " + new Rectangle(pBounds.x, pBounds.y, pBounds.width, pBounds.height)); + System.out.print("Region points: "); + for (i = 0; pPolygon != null && i < pPolygon.npoints - 1; i++) { + System.out.print("(" + pPolygon.xpoints[i] + "," + pPolygon.ypoints[i] + "), "); + } + if (pPolygon != null && pPolygon.npoints > 0) { + System.out.print("(" + pPolygon.xpoints[i] + "," + pPolygon.ypoints[i] + ")"); + } + System.out.println(); + } + + @Override + public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException { + checkBounds(pIndex); + + processImageStarted(pIndex); + + // TODO: Param handling (need not be faked) + + Rectangle frame = getPICTFrame(); + BufferedImage image = getDestination(pParam, getImageTypes(pIndex), getXPtCoord(frame.width), getYPtCoord(frame.height)); + Graphics2D g = image.createGraphics(); + try { + // TODO: Might need to clear background + + g.setTransform(AffineTransform.getScaleInstance(mScreenImageXRatio, mScreenImageYRatio)); + try { + drawOnto(g); + } + catch (IOException e) { + e.printStackTrace(); + } + } + finally { + g.dispose(); + } + + processImageComplete(); + + return image; + } + + public int getWidth(int pIndex) throws IOException { + checkBounds(pIndex); + return getXPtCoord(getPICTFrame().width); + } + + public int getHeight(int pIndex) throws IOException { + checkBounds(pIndex); + return getYPtCoord(getPICTFrame().height); + } + + public Iterator getImageTypes(int pIndex) throws IOException { + // TODO: The images look slightly different in Preview.. Could indicate the color space is wrong... + return Arrays.asList( + ImageTypeSpecifier.createPacked( + ColorSpace.getInstance(ColorSpace.CS_sRGB), + 0xff0000, 0xff00, 0xff, 0xff000000, DataBuffer.TYPE_INT, false + ) + ).iterator(); + } + + public static void main(String[] pArgs) throws IOException { + ImageReader reader = new PICTImageReader(new PICTImageReaderSpi()); + + ImageInputStream input; + String title; + if (pArgs.length >= 1) { + File file = new File(pArgs[0]); + input = ImageIO.createImageInputStream(file); + title = file.getName(); + } + else { + input = ImageIO.createImageInputStream(new ByteArrayInputStream(DATA_V1_OVERPAINTED_ARC)); + title = "PICT test data"; + } + + System.out.println("canRead: " + reader.getOriginatingProvider().canDecodeInput(input)); + + reader.setInput(input); + long start = System.currentTimeMillis(); + BufferedImage image = reader.read(0); + + System.out.println("time: " + (System.currentTimeMillis() - start)); + + showIt(image, title); + + System.out.println("image = " + image); + } + + // Sample data from http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-458.html + // TODO: Create test case(s)! + private static final byte[] DATA_EXT_V2 = { + 0x00, 0x78, /* picture size; don't use this value for picture size */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x00, (byte) 0xA8, /* bounding rectangle of picture at 72 dpi */ + 0x00, 0x11, /* VersionOp opcode; always $0011 for extended version 2 */ + 0x02, (byte) 0xFF, /* Version opcode; always $02FF for extended version 2 */ + 0x0C, 0x00, /* HeaderOp opcode; always $0C00 for extended version 2 */ + /* next 24 bytes contain header information */ + (byte) 0xFF, (byte) 0xFE, /* version; always -2 for extended version 2 */ + 0x00, 0x00, /* reserved */ + 0x00, 0x48, 0x00, 0x00, /* best horizontal resolution: 72 dpi */ + 0x00, 0x48, 0x00, 0x00, /* best vertical resolution: 72 dpi */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* optimal source rectangle for 72 dpi horizontal + and 72 dpi vertical resolutions */ + 0x00, 0x00, /* reserved */ + 0x00, 0x1E, /* DefHilite opcode to use default hilite color */ + 0x00, 0x01, /* Clip opcode to define clipping region for picture */ + 0x00, 0x0A, /* region size */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* bounding rectangle for clipping region */ + 0x00, 0x0A, /* FillPat opcode; fill pattern specified in next 8 bytes */ + 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, /* fill pattern */ + 0x00, 0x34, /* fillRect opcode; rectangle specified in next 8 bytes */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* rectangle to fill */ + 0x00, 0x0A, /* FillPat opcode; fill pattern specified in next 8 bytes */ + (byte) 0x88, 0x22, (byte) 0x88, 0x22, (byte) 0x88, 0x22, (byte) 0x88, 0x22, /* fill pattern */ + 0x00, 0x5C, /* fillSameOval opcode */ + 0x00, 0x08, /* PnMode opcode */ + 0x00, 0x08, /* pen mode data */ + 0x00, 0x71, /* paintPoly opcode */ + 0x00, 0x1A, /* size of polygon */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* bounding rectangle for polygon */ + 0x00, 0x6E, 0x00, 0x02, 0x00, 0x02, 0x00, 0x54, 0x00, 0x6E, 0x00, (byte) 0xAA, 0x00, 0x6E, 0x00, 0x02, /* polygon points */ + 0x00, (byte) 0xFF, /* OpEndPic opcode; end of picture */ + }; + + private static final byte[] DATA_V2 = { + 0x00, 0x78, /* picture size; don't use this value for picture size */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* bounding rectangle of picture */ + 0x00, 0x11, /* VersionOp opcode; always $0x00, 0x11, for version 2 */ + 0x02, (byte) 0xFF, /* Version opcode; always $0x02, 0xFF, for version 2 */ + 0x0C, 0x00, /* HeaderOp opcode; always $0C00 for version 2 */ + /* next 24 bytes contain header information */ + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, /* version; always -1 (long) for version 2 */ + 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, (byte) 0xAA, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, /* fixed-point bounding + rectangle for picture */ + 0x00, 0x00, 0x00, 0x00, /* reserved */ + 0x00, 0x1E, /* DefHilite opcode to use default hilite color */ + 0x00, 0x01, /* Clip opcode to define clipping region for picture */ + 0x00, 0x0A, /* region size */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* bounding rectangle for clipping region */ + 0x00, 0x0A, /* FillPat opcode; fill pattern specifed in next 8 bytes */ + 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, /* fill pattern */ + 0x00, 0x34, /* fillRect opcode; rectangle specified in next 8 bytes */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* rectangle to fill */ + 0x00, 0x0A, /* FillPat opcode; fill pattern specified in next 8 bytes */ + (byte) 0x88, 0x22, (byte) 0x88, 0x22, (byte) 0x88, 0x22, (byte) 0x88, 0x22, /* fill pattern */ + 0x00, 0x5C, /* fillSameOval opcode */ + 0x00, 0x08, /* PnMode opcode */ + 0x00, 0x08, /* pen mode data */ + 0x00, 0x71, /* paintPoly opcode */ + 0x00, 0x1A, /* size of polygon */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* bounding rectangle for polygon */ + 0x00, 0x6E, 0x00, 0x02, 0x00, 0x02, 0x00, 0x54, 0x00, 0x6E, 0x00, (byte) 0xAA, 0x00, 0x6E, 0x00, 0x02, /* polygon points */ + 0x00, (byte) 0xFF, /* OpEndPic opcode; end of picture */ + }; + + private static final byte[] DATA_V1 = { + 0x00, 0x4F, /* picture size; this value is reliable for version 1 pictures */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* bounding rectangle of picture */ + 0x11, /* picVersion opcode for version 1 */ + 0x01, /* version number 1 */ + 0x01, /* ClipRgn opcode to define clipping region for picture */ + 0x00, 0x0A, /* region size */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* bounding rectangle for region */ + 0x0A, /* FillPat opcode; fill pattern specified in next 8 bytes */ + 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, 0x77, (byte) 0xDD, /* fill pattern */ + 0x34, /* fillRect opcode; rectangle specified in next 8 bytes */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* rectangle to fill */ + 0x0A, /* FillPat opcode; fill pattern specified in next 8 bytes */ + (byte) 0x88, 0x22, (byte) 0x88, 0x22, (byte) 0x88, 0x22, (byte) 0x88, 0x22, /* fill pattern */ + 0x5C, /* fillSameOval opcode */ + 0x71, /* paintPoly opcode */ + 0x00, 0x1A, /* size of polygon */ + 0x00, 0x02, 0x00, 0x02, 0x00, 0x6E, 0x00, (byte) 0xAA, /* bounding rectangle for polygon */ + 0x00, 0x6E, 0x00, 0x02, 0x00, 0x02, 0x00, 0x54, 0x00, 0x6E, 0x00, (byte) 0xAA, 0x00, 0x6E, 0x00, 0x02, /* polygon points */ + (byte) 0xFF, /* EndOfPicture opcode; end of picture */ + }; + + // Examples from http://developer.apple.com/technotes/qd/qd_14.html + private static final byte[] DATA_V1_OVAL_RECT = { + 0x00, 0x26, /*size */ + 0x00, 0x0A, 0x00, 0x14, 0x00, (byte) 0xAF, 0x00, 0x78, /* picFrame */ + 0x11, 0x01, /* version 1 */ + 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xFA, 0x01, (byte) 0x90, /* clipRgn -- 10 byte region */ + 0x0B, 0x00, 0x04, 0x00, 0x05, /* ovSize point */ + 0x40, 0x00, 0x0A, 0x00, 0x14, 0x00, (byte) 0xAF, 0x00, 0x78, /* frameRRect rectangle */ + (byte) 0xFF, /* fin */ + }; + + private static final byte[] DATA_V1_OVERPAINTED_ARC = { + 0x00, 0x36, /* size */ + 0x00, 0x0A, 0x00, 0x14, 0x00, (byte) 0xAF, 0x00, 0x78, /* picFrame */ + 0x11, 0x01, /* version 1 */ + 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xFA, 0x01, (byte) 0x90, /* clipRgn -- 10 byte region */ + 0x61, 0x00, 0x0A, 0x00, 0x14, 0x00, (byte) 0xAF, 0x00, 0x78, 0x00, 0x03, 0x00, 0x2D, /* paintArc rectangle,startangle,endangle */ + 0x08, 0x00, 0x0A, /* pnMode patXor -- note that the pnMode comes before the pnPat */ + 0x09, (byte) 0xAA, 0x55, (byte) 0xAA, 0x55, (byte) 0xAA, 0x55, (byte) 0xAA, 0x55, /* pnPat gray */ + 0x69, 0x00, 0x03, 0x00, 0x2D, /* paintSameArc startangle,endangle */ + (byte) 0xFF, /* fin */ + }; + + private static final byte[] DATA_V1_COPY_BITS = { + 0x00, 0x48, /* size */ + 0x00, 0x0A, 0x00, 0x14, 0x00, (byte) 0xAF, 0x00, 0x78, /* picFrame */ + 0x11, 0x01, /* version 1 */ + 0x01, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xFA, 0x01, (byte) 0x90, /* clipRgn -- 10 byte region */ + 0x31, 0x00, 0x0A, 0x00, 0x14, 0x00, (byte) 0xAF, 0x00, 0x78, /* paintRect rectangle */ + (byte) 0x90, 0x00, 0x02, 0x00, 0x0A, 0x00, 0x14, 0x00, 0x0F, 0x00, 0x1C, /* BitsRect rowbytes bounds (note that bounds is wider than smallr) */ + 0x00, 0x0A, 0x00, 0x14, 0x00, 0x0F, 0x00, 0x19, /* srcRect */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x1E, /* dstRect */ + 0x00, 0x06, /* mode=notSrcXor */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5 rows of empty bitmap (we copied from a + still-blank window) */ + (byte) 0xFF, /* fin */ + }; +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReaderSpi.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReaderSpi.java new file mode 100755 index 00000000..f7529745 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReaderSpi.java @@ -0,0 +1,123 @@ +/* + * 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.pict; + +import javax.imageio.ImageReader; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; +import java.io.EOFException; +import java.util.Locale; + +/** + * PICTImageReaderSpi + *

+ * + * @author Harald Kuhr + * @version $Id: PICTImageReaderSpi.java,v 1.0 28.feb.2006 19:21:05 haku Exp$ + */ +public class PICTImageReaderSpi extends ImageReaderSpi { + + /** + * Creates an PICTImageReaderSpi + */ + public PICTImageReaderSpi() { + super( + "TwelveMonkeys", + "2.2", + new String[]{"pct", "PCT", "pict", "PICT"}, + new String[]{"pct", "pict"}, + new String[]{"image/pict", "image/x-pict"}, + "com.twelvemkonkeys.imageio.plugins.pict.PICTImageReader", + STANDARD_INPUT_TYPE, + new String[]{"com.twelvemkonkeys.imageio.plugins.pict.PICTImageWriterSpi"}, + true, null, null, null, null, + true, null, null, null, null + ); + } + + public boolean canDecodeInput(final Object pSource) throws IOException { + if (!(pSource instanceof ImageInputStream)) { + return false; + } + + ImageInputStream stream = (ImageInputStream) pSource; + + stream.mark(); + try { + if (isPICT(stream)) { + // If PICT Clipping format, return true immediately + return true; + } + else { + // Skip header 512 bytes for file-based streams + stream.reset(); + PICTImageReader.skipNullHeader(stream); + } + return isPICT(stream); + } + catch (EOFException ignore) { + return false; + } + finally { + stream.reset(); + } + } + + private boolean isPICT(final ImageInputStream pStream) throws IOException { + // Size may be 0, so we can't use this for validation... + pStream.readUnsignedShort(); + + // Sanity check bounding box + int y1 = pStream.readUnsignedShort(); + int x1 = pStream.readUnsignedShort(); + // TODO: Figure out if frame can ever start at negative bounds... + // if (x1 != 0 || y1 != 0) { + // return false; + // } + + int y2 = pStream.readUnsignedShort(); + int x2 = pStream.readUnsignedShort(); + if (x2 - x1 < 0 || y2 - y1 < 0) { + return false; + } + + int magic = pStream.readInt(); + + return (magic & 0xffff0000) == PICT.MAGIC_V1 || magic == PICT.MAGIC_V2; + } + + public ImageReader createReaderInstance(final Object pExtension) throws IOException { + return new PICTImageReader(this); + } + + public String getDescription(final Locale pLocale) { + return "Apple Mac Paint Picture (PICT) image reader"; + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageWriter.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageWriter.java new file mode 100755 index 00000000..9149fb5b --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageWriter.java @@ -0,0 +1,414 @@ +/* +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. + + +Parts of this software is based on JVG/JIS. +See http://www.cs.hut.fi/~framling/JVG/index.html for more information. +Redistribution under BSD authorized by Kary Främling: + +Copyright (c) 2003, Kary Främling +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 of the JIS/JVG 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.pict; + +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 javax.imageio.stream.ImageOutputStream; +import java.awt.color.ColorSpace; +import java.awt.image.*; +import java.io.*; + +/** + * Writer for Apple Mac Paint Picture (PICT) format. + *

+ * Images are stored using the "opDirectBitsRect" opcode, which directly + * stores RGB values (using PackBits run-length encoding). + * + * @author Kary Främling + * @author Harald Kuhr + * @version $Id: PICTWriter.java,v 1.0 05.apr.2006 15:20:48 haku Exp$ + */ +public class PICTImageWriter extends ImageWriterBase { + + // TODO: Inline these? + private int mRowBytes; + private byte[] mScanlineBytes; + private int mScanWidthLeft; + + public PICTImageWriter() { + this(null); + } + + /** + * Constructs an {@code ImageWriter} and sets its + * {@code originatingProvider} instance variable to the + * supplied value. + *

+ *

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 PICTImageWriter(final ImageWriterSpi pProvider) { + super(pProvider); + } + + private void writePICTHeader(RenderedImage pImage) throws IOException { + // TODO: Make 512 byte header optional + // Write empty 512-byte header + byte[] buf = new byte[PICT.PICT_NULL_HEADER_SIZE]; + mImageOutput.write(buf); + + // Write out the size, leave as 0, this is ok + mImageOutput.writeShort(0); + + // Write image frame (same as image bounds) + mImageOutput.writeShort(0); + mImageOutput.writeShort(0); + mImageOutput.writeShort(pImage.getHeight()); + mImageOutput.writeShort(pImage.getWidth()); + + // Write version, version 2 + mImageOutput.writeShort(PICT.OP_VERSION); + mImageOutput.writeShort(PICT.OP_VERSION_2); + + // Version 2 HEADER_OP, extended version. + mImageOutput.writeShort(PICT.OP_HEADER_OP); + mImageOutput.writeInt(PICT.HEADER_V2_EXT); // incl 2 bytes reseverd + + // Image resolution, 72 dpi + mImageOutput.writeShort(PICT.MAC_DEFAULT_DPI); + mImageOutput.writeShort(0); + mImageOutput.writeShort(PICT.MAC_DEFAULT_DPI); + mImageOutput.writeShort(0); + + // Optimal source rectangle (same as image bounds) + mImageOutput.writeShort(0); + mImageOutput.writeShort(0); + mImageOutput.writeShort(pImage.getHeight()); + mImageOutput.writeShort(pImage.getWidth()); + + // Reserved (4 bytes) + mImageOutput.writeInt(0); + + // TODO: The header really ends here... + + // Highlight + mImageOutput.writeShort(PICT.OP_DEF_HILITE); + + // Set the clip rectangle + mImageOutput.writeShort(PICT.OP_CLIP_RGN); + mImageOutput.writeShort(10); + mImageOutput.writeShort(0); + mImageOutput.writeShort(0); + mImageOutput.writeShort(pImage.getHeight()); + mImageOutput.writeShort(pImage.getWidth()); + + // Pixmap operation + mImageOutput.writeShort(PICT.OP_DIRECT_BITS_RECT); + + // PixMap pointer (always 0x000000FF); + mImageOutput.writeInt(0x000000ff); + + // Write rowBytes, this is 4 times the width. + // Set the high bit, to indicate a PixMap. + mRowBytes = 4 * pImage.getWidth(); + mImageOutput.writeShort(0x8000 | mRowBytes); + + // Write bounds rectangle (same as image bounds) + mImageOutput.writeShort(0); + mImageOutput.writeShort(0); + mImageOutput.writeShort(pImage.getHeight()); // TODO: Handle overflow? + mImageOutput.writeShort(pImage.getWidth()); + + // PixMap record version + mImageOutput.writeShort(0); + + // Packing format (always 4: PackBits) + mImageOutput.writeShort(4); + + // Size of packed data (leave as 0) + mImageOutput.writeInt(0); + + // Pixmap resolution, 72 dpi + mImageOutput.writeShort(PICT.MAC_DEFAULT_DPI); + mImageOutput.writeShort(0); + mImageOutput.writeShort(PICT.MAC_DEFAULT_DPI); + mImageOutput.writeShort(0); + + // Pixel type, 16 is allright for direct pixels + mImageOutput.writeShort(16); + + // Pixel size + mImageOutput.writeShort(32); + + // TODO: Allow alpha? Allow 5 bit per pixel component (16 bit)? + // Pixel component count + mImageOutput.writeShort(3); + + // Pixel component size + mImageOutput.writeShort(8); + + // PlaneBytes, ignored for now + mImageOutput.writeInt(0); + + // TODO: Allow IndexColorModel? + // ColorTable record (for RGB direct pixels, just write 0) + mImageOutput.writeInt(0); + + // Reserved (4 bytes) + mImageOutput.writeInt(0); + + // Source and dest rect (both are same as image bounds) + mImageOutput.writeShort(0); + mImageOutput.writeShort(0); + mImageOutput.writeShort(pImage.getHeight()); + mImageOutput.writeShort(pImage.getWidth()); + + mImageOutput.writeShort(0); + mImageOutput.writeShort(0); + mImageOutput.writeShort(pImage.getHeight()); + mImageOutput.writeShort(pImage.getWidth()); + + // Transfer mode + mImageOutput.writeShort(QuickDraw.SRC_COPY); + + // TODO: Move to writePICTData? + // TODO: Alpha support + // Set up the buffers for storing scanline bytes + mScanlineBytes = new byte[3 * pImage.getWidth()]; + mScanWidthLeft = pImage.getWidth(); + } + + private void writePICTData(int x, int y, int w, int h, ColorModel model, + byte[] pixels, int off, int scansize) throws IOException { + + ByteArrayOutputStream bytes = new FastByteArrayOutputStream(mScanlineBytes.length / 2); + + int components = model.getNumComponents(); + + // TODO: Clean up, as we only have complete scanlines + + // Fill the scanline buffer. We get problems if ever we have several + // lines (h > 1) and (w < width). This should never be the case. + for (int i = 0; i < h; i++) { + // Reduce the counter of bytes left on the scanline. + mScanWidthLeft -= w; + + // Treat the scanline. + for (int j = 0; j < w; j++) { + if (model instanceof ComponentColorModel && model.getColorSpace().getType() == ColorSpace.TYPE_RGB) { + // TODO: Component order? + // TODO: Alpha support + mScanlineBytes[x + j] = pixels[off + i * scansize * components + components * j + 2]; + mScanlineBytes[x + w + j] = pixels[off + i * scansize * components + components * j + 1]; + mScanlineBytes[x + 2 * w + j] = pixels[off + i * scansize * components + components * j]; + } + else { + int rgb = model.getRGB(pixels[off + i * scansize + j] & 0xFF); + // Set red, green and blue components. + mScanlineBytes[x + j] = (byte) ((rgb >> 16) & 0xFF); + mScanlineBytes[x + w + j] = (byte) ((rgb >> 8) & 0xFF); + mScanlineBytes[x + 2 * w + j] = (byte) (rgb & 0xFF); + } + + } + + // If we have a complete scanline, then pack it and write it out. + if (mScanWidthLeft == 0) { + // Pack using PackBitsEncoder/EncoderStream + bytes.reset(); + DataOutput packBits = new DataOutputStream(new EncoderStream(bytes, new PackBitsEncoder(), true)); + + packBits.write(mScanlineBytes); + + if (mRowBytes > 250) { + mImageOutput.writeShort(bytes.size()); + } + else { + mImageOutput.writeByte(bytes.size()); + } + + bytes.writeTo(IIOUtil.createStreamAdapter(mImageOutput)); + + mScanWidthLeft = w; + } + } + } + + private void writePICTData(int x, int y, int w, int h, ColorModel model, + int[] pixels, int off, int scansize) throws IOException { + + ByteArrayOutputStream bytes = new FastByteArrayOutputStream(mScanlineBytes.length / 2); + + // TODO: Clean up, as we only have complete scanlines + + // Fill the scanline buffer. We get problems if ever we have several + // lines (h > 1) and (w < width). This should never be the case. + for (int i = 0; i < h; i++) { + // Reduce the counter of bytes left on the scanline. + mScanWidthLeft -= w; + + // Treat the scanline. + for (int j = 0; j < w; j++) { + int rgb = model.getRGB(pixels[off + i * scansize + j]); + + // Set red, green and blue components. + mScanlineBytes[x + j] = (byte) ((rgb >> 16) & 0xFF); + mScanlineBytes[x + w + j] = (byte) ((rgb >> 8) & 0xFF); + mScanlineBytes[x + 2 * w + j] = (byte) (rgb & 0xFF); + } + + // If we have a complete scanline, then pack it and write it out. + if (mScanWidthLeft == 0) { + // Pack using PackBitsEncoder/EncoderStream + bytes.reset(); + DataOutput packBits = new DataOutputStream(new EncoderStream(bytes, new PackBitsEncoder(), true)); + + packBits.write(mScanlineBytes); + + if (mRowBytes > 250) { + mImageOutput.writeShort(bytes.size()); + } + else { + mImageOutput.writeByte(bytes.size()); + } + + bytes.writeTo(IIOUtil.createStreamAdapter(mImageOutput)); + + mScanWidthLeft = w; + } + } + } + + private void writePICTTrailer() throws IOException { + // Write out end opcode. Be sure to be word-aligned. + long length = mImageOutput.length(); + if (length == -1) { + throw new IIOException("Cannot write trailer without knowing length"); + } + + if ((length & 1) > 0) { + mImageOutput.writeByte(0); + } + mImageOutput.writeShort(PICT.OP_END_OF_PICTURE); + } + + public void write(IIOMetadata pStreamMetadata, IIOImage pImage, ImageWriteParam pParam) throws IOException { + assertOutput(); + + if (pImage.hasRaster()) { + throw new UnsupportedOperationException("Cannot write raster"); + } + + processImageStarted(0); + + RenderedImage image = pImage.getRenderedImage(); + writePICTHeader(image); + + // NOTE: getRaster is much faster than getData, as it does no copying + Raster raster = image instanceof BufferedImage ? ((BufferedImage) image).getRaster() : image.getData(); + DataBuffer buf = raster.getDataBuffer(); + if (buf instanceof DataBufferByte) { + writePICTData(0, 0, image.getWidth(), image.getHeight(), + image.getColorModel(), ((DataBufferByte) buf).getData(), + 0, image.getWidth()); + } + else if (buf instanceof DataBufferInt) { + writePICTData(0, 0, image.getWidth(), image.getHeight(), + image.getColorModel(), ((DataBufferInt) buf).getData(), + 0, image.getWidth()); + } + else { + throw new IIOException("DataBuffer type " + buf.getDataType() + " not supported"); + } + // TODO: Support 16 bit USHORT type + + writePICTTrailer(); + + processImageComplete(); + } + + 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 static void main(String[] pArgs) throws IOException { + System.out.print("Reading image.. "); + + BufferedImage image = ImageIO.read(new File(pArgs[0])); + + System.out.println("image read"); + + System.out.println("image: " + image); + + ImageWriter writer = new PICTImageWriter(null); + ImageOutputStream stream = ImageIO.createImageOutputStream(new File(pArgs[1])); + writer.setOutput(stream); + writer.write(image); + stream.close(); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageWriterSpi.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageWriterSpi.java new file mode 100755 index 00000000..b349c56b --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageWriterSpi.java @@ -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.pict; + +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriter; +import javax.imageio.spi.ImageWriterSpi; +import java.io.IOException; +import java.util.Locale; + +/** + * PICTImageWriterSpi + *

+ * + * @author Harald Kuhr + * @version $Id: PICTImageWriterSpi.java,v 1.0 02.mar.2006 19:21:05 haku Exp$ + */ +public class PICTImageWriterSpi extends ImageWriterSpi { + + /** + * Creates an PICTImageWriterSpi + */ + public PICTImageWriterSpi() { + super( + "TwelveMonkeys", + "2.0", + new String[]{"pct", "PCT", + "pict", "PICT"}, + new String[]{"pct", "pict"}, + new String[]{"image/pict", "image/x-pict"}, + "com.twelvemonkeys.imageio.plugins.pict.PICTImageWriter", + STANDARD_OUTPUT_TYPE, + new String[]{"com.twelvemonkeys.imageio.plugins.pict.PICTImageReaderSpi"}, + true, null, null, null, null, + true, null, null, null, null + ); + } + + public boolean canEncodeImage(ImageTypeSpecifier pType) { + // TODO: FixMe + return true; + } + + public ImageWriter createWriterInstance(Object pExtension) throws IOException { + return new PICTImageWriter(); + } + + public String getDescription(Locale pLocale) { + return "Apple Mac Paint Picture (PICT) image writer"; + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTUtil.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTUtil.java new file mode 100755 index 00000000..ef503715 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTUtil.java @@ -0,0 +1,264 @@ +/* + * 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.pict; + +import javax.imageio.IIOException; +import java.awt.*; +import java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; +import java.io.DataInput; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +/** + * PICTUtil + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PICTUtil.java,v 1.0 Feb 16, 2009 8:46:27 PM haraldk Exp$ + */ +final class PICTUtil { + + private static final String ENC_MAC_ROMAN = "MacRoman"; + + public static final String ENCODING = initEncoding(); + + private static String initEncoding() { + try { + new String("\uF8FF".getBytes(), ENC_MAC_ROMAN); + return ENC_MAC_ROMAN; + } + catch (UnsupportedEncodingException e) { + return "ISO-8859-1"; + } + } + + /** + * Reads a fixed point number from the given stream. + * + * @param pStream the input stream + * @return the number as a {@code double}. + * + * @throws java.io.IOException if an I/O error occurs during read + */ + public static double readFixedPoint(final DataInput pStream) throws IOException { + return pStream.readInt() / (double) (1 << 16); + } + + static String readIdString(final DataInput pStream) throws IOException { + byte[] bytes = new byte[4]; + pStream.readFully(bytes); + return new String(bytes, "ASCII"); + } + + /** + * Reads a dimension from the given stream. + * + * @param pStream the input stream + * @return the dimension read + * + * @throws java.io.IOException if an I/O error occurs during read + */ + public static Dimension readDimension(final DataInput pStream) throws IOException { + final int h = pStream.readShort() ; + final int v = pStream.readShort() ; + return new Dimension(h,v); + } + + /** + * Reads a 32 byte fixed length Pascal string from the given input. + * The input stream must be positioned at the length byte of the text, + * the text will be no longer than 31 characters long. + * + * @param pStream the input stream + * @return the text read + * + * @throws IOException if an I/O exception occurs during reading + */ + public static String readStr31(final DataInput pStream) throws IOException { + String text = readPascalString(pStream); + int length = 31 - text.length(); + if (length < 0) { + throw new IOException("String length exceeds maximum (31): " + text.length()); + } + pStream.skipBytes(length); + return text; + } + + /** + * Reads a Pascal String from the given strean. + * The input stream must be positioned at the length byte of the text, + * which can thus be a maximum of 255 characters long. + * + * @param pStream the input stream + * @return the text read + * + * @throws IOException if an I/O exception occurs during reading + */ + public static String readPascalString(final DataInput pStream) throws IOException { + // Get as many bytes as indicated by byte count + int length = pStream.readUnsignedByte(); + + byte[] bytes = new byte[length]; + pStream.readFully(bytes, 0, length); + + return new String(bytes, ENCODING); + } + + /** + * Reads a {@link Pattern pattern} from the given stream. + * + * @param pStream the input stream + * @return the pattern read + * + * @throws java.io.IOException if an I/O error occurs during read + */ + public static Pattern readPattern(final DataInput pStream) throws IOException { + // Get the data (8 bytes) + byte[] data = new byte[8]; + pStream.readFully(data); + return new BitMapPattern(data); + } + + /** + * Reads a variable width {@link Pattern color pattern} from the given stream + * + * @param pStream the input stream + * @return the pattern read + * + * @throws java.io.IOException if an I/O error occurs during read + */ + /* + http://developer.apple.com/DOCUMENTATION/mac/QuickDraw/QuickDraw-461.html#MARKER-9-243 + IF patType = ditherPat + THEN + PatType: word; {pattern type = 2} + Pat1Data: Pattern; {old pattern data} + RGB: RGBColor; {desired RGB for pattern} + ELSE + PatType: word; {pattern type = 1} + Pat1Data: Pattern; {old pattern data} + PixMap: PixMap; + ColorTable: ColorTable; + PixData: PixData; + END; + */ + public static Pattern readColorPattern(final DataInput pStream) throws IOException { + short type = pStream.readShort(); + + Pattern pattern; + Pattern fallback = readPattern(pStream); + + if (type == 1) { + // TODO: This is foobar... + // PixMap + // ColorTable + // PixData + throw new IIOException(String.format("QuickDraw pattern type '0x%04x' not implemented (yet)", type)); + } + else if (type == 2) { + Color color = readRGBColor(pStream); + pattern = new PixMapPattern(color, fallback); + } + else { + throw new IIOException(String.format("Unknown QuickDraw pattern type '0x%04x'", type)); + } + + return pattern; + } + + /** + * Reads an {@link RGBColor} record from the given stream. + * + * @param pStream the input stream + * @return the color read + * + * @throws java.io.IOException if an I/O error occurs during read + */ + /* + http://developer.apple.com/DOCUMENTATION/mac/QuickDraw/QuickDraw-269.html#HEADING269-11 + RGBColor = + RECORD + red: Integer; {red component} + green: Integer; {green component} + blue: Integer; {blue component} + END; + */ + public static Color readRGBColor(final DataInput pStream) throws IOException { + short r = pStream.readShort(); + short g = pStream.readShort(); + short b = pStream.readShort(); + + return new RGBColor(r, g, b); + } + + /** + * Reads a {@code ColorTable} data structure from the given stream. + * + * @param pStream the input stream + * @param pPixelSize the pixel size + * @return the indexed color model created from the {@code ColorSpec} records read. + * + * @throws java.io.IOException if an I/O error occurs during read + */ + /* + http://developer.apple.com/DOCUMENTATION/mac/QuickDraw/QuickDraw-269.html#HEADING269-11 + ColorSpec = + RECORD + value: Integer; {index or other value} + rgb: RGBColor; {true color} + END; + + ColorTable = + RECORD + ctSeed: LongInt; {unique identifier from table} + ctFlags: Integer; {contains flags describing the ctTable field; } + { clear for a PixMap record} + ctSize: Integer; {number of entries in the next field minus 1} + ctTable: cSpecArray; {an array of ColorSpec records} + END; + */ + public static IndexColorModel readColorTable(final DataInput pStream, final int pPixelSize) throws IOException { + // TODO: Do we need to support these? + /*int seed = */pStream.readInt(); + /*int flags = */pStream.readUnsignedShort(); + int size = pStream.readUnsignedShort() + 1; // data is size - 1 + + int[] colors = new int[size]; + + for (int i = 0; i < size ; i++) { + // Read ColorSpec records + int index = pStream.readUnsignedShort(); + Color color = readRGBColor(pStream); + colors[index] = color.getRGB(); + } + + return new IndexColorModel(pPixelSize, size, colors, 0, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/Pattern.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/Pattern.java new file mode 100755 index 00000000..d7d073d0 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/Pattern.java @@ -0,0 +1,64 @@ +/* + * 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.pict; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.geom.AffineTransform; +import java.awt.image.*; +import java.util.Collections; + +/** + * Pattern + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: Pattern.java,v 1.0 Oct 9, 2007 1:21:38 AM haraldk Exp$ + */ +abstract class Pattern implements Paint { + private final Paint mPaint; + + Pattern(final Paint pPaint) { + mPaint = pPaint; + } + + public PaintContext createContext(final ColorModel pModel, final Rectangle pDeviceBounds, + final Rectangle2D pUserBounds, final AffineTransform pTransform, + final RenderingHints pHints) { + return mPaint.createContext( + pModel, pDeviceBounds, + pUserBounds, pTransform, + pHints != null ? pHints : new RenderingHints(Collections.emptyMap()) + ); + } + + public int getTransparency() { + return mPaint.getTransparency(); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PenState.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PenState.java new file mode 100755 index 00000000..8fd251f0 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PenState.java @@ -0,0 +1,52 @@ +/* + * 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.pict; + +import java.awt.*; + +/** + * PenState + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PenState.java,v 1.0 Oct 9, 2007 1:56:33 AM haraldk Exp$ + */ +class PenState { + public final Point mPenLocation; /* pen location */ + public final Dimension mPenSize; /* pen size */ + public final int mPenMode; /* pen's pattern mode */ + public final Pattern mPenPattern; /* pen pattern */ + + public PenState(final Point pPenLocation, final int pPenMode, final Pattern pPenPattern, final Dimension pPenSize) { + mPenLocation = pPenLocation; + mPenMode = pPenMode; + mPenPattern = pPenPattern; + mPenSize = pPenSize; + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PixMapPattern.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PixMapPattern.java new file mode 100755 index 00000000..470a04a0 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PixMapPattern.java @@ -0,0 +1,54 @@ +/* + * 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.pict; + +import java.awt.*; + +/** + * PixMapPattern + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PixMapPattern.java,v 1.0 Mar 1, 2009 11:36:10 PM haraldk Exp$ + */ +final class PixMapPattern extends Pattern { + private final Pattern mFallback; + + PixMapPattern(final Paint pPaint, final Pattern pBitMapFallback) { + super(pPaint); + mFallback = pBitMapFallback; + } + + /** + * @return the fallback B/W pattern + */ + public Pattern getPattern() { + return mFallback; + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QDTest.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QDTest.java new file mode 100755 index 00000000..a58cbeeb --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QDTest.java @@ -0,0 +1,130 @@ +/* + * 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.pict; + +import com.twelvemonkeys.image.ConvolveTester; + +import java.awt.image.BufferedImage; +import java.awt.*; + +/** + * QDTest + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: QDTest.java,v 1.0 Oct 10, 2007 6:06:55 PM haraldk Exp$ + */ +public class QDTest { + public static void main(String[] pArgs) { + BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB); + + Graphics2D g = image.createGraphics(); +// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + QuickDrawContext context = new QuickDrawContext(g); + try { + // Fill background, as Xor don't work with transparent bg + context.fillRect(new Rectangle(200, 200), QuickDraw.WHITE); + + context.moveTo(10, 10); + context.lineTo(10, 190); + context.lineTo(190, 190); + context.lineTo(190, 10); + context.lineTo(10, 10); + + context.moveTo(10, 10); + context.lineTo(190, 190); + + context.setPenSize(new Dimension(2, 2)); + context.frameRect(new Rectangle(15, 15, 20, 20)); + context.paintRect(new Rectangle(15, 45, 20, 20)); + context.fillRect(new Rectangle(15, 75, 20, 20), QuickDraw.DARK_GRAY); + context.fillRect(new Rectangle(12, 102, 26, 26), new BitMapPattern(Color.GRAY)); + context.eraseRect(new Rectangle(15, 105, 20, 20)); + context.fillRect(new Rectangle(12, 132, 26, 8), QuickDraw.LIGT_GRAY); + context.fillRect(new Rectangle(12, 140, 26, 10), new BitMapPattern(Color.RED)); + context.fillRect(new Rectangle(12, 150, 26, 8), QuickDraw.DARK_GRAY); + context.invertRect(new Rectangle(15, 135, 20, 20)); + + context.setPenSize(new Dimension(10, 10)); + context.moveTo(80, 30); + context.line(80, 20); + context.move(20, 0); + context.line(0, -25); + + context.setPenPattern(QuickDraw.GRAY); + context.moveTo(80, 70); + context.line(80, 20); + context.move(20, 0); + context.line(0, -25); + + context.setPenPattern(new BitMapPattern(Color.GRAY)); + context.moveTo(80, 110); + context.line(80, 20); + context.move(20, 0); + context.line(0, -25); + + context.setPenPattern(new BitMapPattern(Color.RED)); + context.moveTo(80, 150); + context.line(80, 20); + context.move(20, 0); + context.line(0, -25); + + context.setPenPattern(new BitMapPattern(Color.ORANGE)); + context.setPenSize(new Dimension(2, 2)); + context.frameRoundRect(new Rectangle(45, 15, 20, 20), 4, 4); + context.setPenPattern(new BitMapPattern(Color.DARK_GRAY)); + context.paintOval(new Rectangle(45, 45, 20, 20)); + context.invertArc(new Rectangle(45 + 1, 45, 20, 20), 45, 90); + context.frameArc(new Rectangle(45 - 1, 75, 20, 20), 45, -270); + context.fillArc(new Rectangle(45 + 1, 75, 20, 20), 45, 90, new BitMapPattern(Color.RED)); + + context.invertPoly(new Polygon(new int[]{43, 55, 67}, new int[]{125, 103, 125}, 3)); + context.setPenPattern(new BitMapPattern(Color.ORANGE)); + Polygon star = new Polygon( + new int[]{43, 52, 55, 58, 68, 59, 63, 55, 47, 51}, + new int[]{143, 143, 133, 143, 143, 148, 157, 152, 157, 148}, + 10 + ); + context.paintPoly(star); + context.setPenNormal(); + context.framePoly(star); + + // TODO: FixMe: Seems like rectangle should be INSIDE? Or at least, one pixel less than AWT thinks.. +// context.frameRoundRect(new Rectangle(20, 10, 100, 165), 5, 4); + + context.moveTo(15, 185); + context.drawString("Java QuickDraw test"); + } + finally { + context.closePicture(); + } + + ConvolveTester.showIt(image, "QuickDraw Test"); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTBMPDecompressor.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTBMPDecompressor.java new file mode 100755 index 00000000..c6e0d7dc --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTBMPDecompressor.java @@ -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.plugins.pict; + +import com.twelvemonkeys.io.FastByteArrayOutputStream; +import com.twelvemonkeys.io.LittleEndianDataOutputStream; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; + +/** + * QTBMPDecompressor + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: QTBMPDecompressor.java,v 1.0 Feb 16, 2009 9:18:28 PM haraldk Exp$ + */ +final class QTBMPDecompressor extends QTDecompressor { + + public boolean canDecompress(final QuickTime.ImageDesc pDescription) { + return QuickTime.VENDOR_APPLE.equals(pDescription.compressorVendor) && "WRLE".equals(pDescription.compressorIdentifer) + && "bmp ".equals(idString(pDescription.extraDesc, 4)); + } + + private static String idString(final byte[] pData, final int pOffset) { + try { + return new String(pData, pOffset, 4, "ASCII"); + } + catch (UnsupportedEncodingException e) { + throw new Error("ASCII charset must always be supported", e); + } + } + + public BufferedImage decompress(final QuickTime.ImageDesc pDescription, final InputStream pStream) throws IOException { + return ImageIO.read(new SequenceInputStream(fakeBMPHeader(pDescription), pStream)); + } + + private InputStream fakeBMPHeader(final QuickTime.ImageDesc pDescription) throws IOException { + int bmpHeaderSize = 14; + int dibHeaderSize = 12; // 12: OS/2 V1 + ByteArrayOutputStream out = new FastByteArrayOutputStream(bmpHeaderSize + dibHeaderSize); + + LittleEndianDataOutputStream stream = new LittleEndianDataOutputStream(out); + + // BMP header + stream.writeByte('B'); + stream.writeByte('M'); + + stream.writeInt(pDescription.dataSize + bmpHeaderSize + dibHeaderSize); // Data size + BMP header + DIB header + + stream.writeShort(0x0); // Reserved + stream.writeShort(0x0); // Reserved + + stream.writeInt(bmpHeaderSize + dibHeaderSize); // Image offset + + // DIB header + stream.writeInt(dibHeaderSize); // DIB header size + + stream.writeShort(pDescription.width); + stream.writeShort(pDescription.height); + + stream.writeShort(1); // Planes, only legal value: 1 + stream.writeShort(pDescription.depth); // Bit depth + + return new ByteArrayInputStream(out.toByteArray()); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTDecompressor.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTDecompressor.java new file mode 100755 index 00000000..502276cf --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTDecompressor.java @@ -0,0 +1,64 @@ +/* + * 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.pict; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; + +/** + * Abstract base class for a stateless image decompressor, for use by {@link QuickTime}. + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: QTDecompressor.java,v 1.0 Feb 16, 2009 7:21:27 PM haraldk Exp$ + */ +abstract class QTDecompressor { + + /** + * Returns wether this decompressor is capable of decompressing the image + * data described by the given image description. + * + * @param pDescription the image description ({@code 'idsc'} Atom). + * @return {@code true} if this decompressor is capable of decompressing + * he data in the given image description, otherwise {@code false}. + */ + public abstract boolean canDecompress(QuickTime.ImageDesc pDescription); + + /** + * Decompresses an image. + * + * @param pDescription the image description ({@code 'idsc'} Atom). + * @param pStream the image data stream + * @return the decompressed image + * + * @throws java.io.IOException if an I/O exception occurs during reading. + */ + public abstract BufferedImage decompress(QuickTime.ImageDesc pDescription, InputStream pStream) throws IOException; +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTGenericDecompressor.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTGenericDecompressor.java new file mode 100755 index 00000000..74b71bbf --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTGenericDecompressor.java @@ -0,0 +1,51 @@ +/* + * 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.pict; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; + +/** + * QTGenericDecompressor + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: QTGenericDecompressor.java,v 1.0 Feb 16, 2009 9:26:13 PM haraldk Exp$ + */ +final class QTGenericDecompressor extends QTDecompressor { + public boolean canDecompress(final QuickTime.ImageDesc pDescription) { + return true; + } + + public BufferedImage decompress(final QuickTime.ImageDesc pDescription, final InputStream pStream) throws IOException { + return ImageIO.read(pStream); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTRAWDecompressor.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTRAWDecompressor.java new file mode 100755 index 00000000..3c026c97 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QTRAWDecompressor.java @@ -0,0 +1,90 @@ +/* + * 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.pict; + +import javax.imageio.IIOException; +import java.awt.*; +import java.awt.color.ColorSpace; +import java.awt.image.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * QTRAWDecompressor + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: QTRAWDecompressor.java,v 1.0 Feb 16, 2009 9:29:18 PM haraldk Exp$ + */ +final class QTRAWDecompressor extends QTDecompressor { + // TODO: Create a RAWImageReader for ImageIO to delegate to? + // - Would have to require a parameter controlling bit depth and pixel layout + // - Have a look at com.sun.media.imageio.stream.RawImageInputStream... + // TODO: Support different bit depths + + public boolean canDecompress(final QuickTime.ImageDesc pDescription) { + return QuickTime.VENDOR_APPLE.equals(pDescription.compressorVendor) + && "raw ".equals(pDescription.compressorIdentifer) + && (pDescription.depth == 24 || pDescription.depth == 32); + } + + public BufferedImage decompress(final QuickTime.ImageDesc pDescription, final InputStream pStream) throws IOException { + DataInputStream stream = new DataInputStream(pStream); + + byte[] data = new byte[pDescription.dataSize]; + stream.readFully(data, 0, pDescription.dataSize); + stream.close(); + + DataBuffer buffer = new DataBufferByte(data, data.length); + + WritableRaster raster; + // TODO: Depth parameter can be 1-32 (color) or 33-40 (grayscale) + switch (pDescription.depth) { + case 24: + raster = Raster.createInterleavedRaster(buffer, pDescription.width, pDescription.height, pDescription.width * 3, 3, new int[] {0, 1, 2}, null); + break; + case 32: + raster = Raster.createInterleavedRaster(buffer, pDescription.width, pDescription.height, pDescription.width * 4, 4, new int[] {1, 2, 3, 0}, null); + break; + default: + throw new IIOException("Unsupported RAW depth: " + pDescription.depth); + } + + ColorModel cm = new ComponentColorModel( + ColorSpace.getInstance(ColorSpace.CS_sRGB), + pDescription.depth == 32, + false, + Transparency.TRANSLUCENT, + DataBuffer.TYPE_BYTE + ); + + return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDraw.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDraw.java new file mode 100755 index 00000000..1c6809a3 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDraw.java @@ -0,0 +1,108 @@ +/* + * 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.pict; + +import java.awt.image.*; +import java.awt.*; + +/** + * QuickDraw constants. + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: QuickDraw.java,v 1.0 Oct 9, 2007 1:15:09 AM haraldk Exp$ + */ +interface QuickDraw { + // Reversed from default BufferedImage.TYPE_BYTE_BINARY + IndexColorModel MONOCHROME = new IndexColorModel(1, 2, new int[] {0xffffff, 0x00000000}, 0, false, -1, DataBuffer.TYPE_BYTE); + + /** QuickDraw {@code white} pattern */ + Pattern WHITE = new BitMapPattern(Color.WHITE); + + /** QuickDraw {@code black} pattern */ + Pattern BLACK = new BitMapPattern(Color.BLACK); + + /** QuickDraw {@code gray} pattern */ + Pattern GRAY = new BitMapPattern(0xAA55AA55); + + /** QuickDraw {@code ltGray} pattern */ + Pattern LIGT_GRAY = new BitMapPattern(0x88228822); + + /** QuickDraw {@code dkGray} pattern */ + Pattern DARK_GRAY = new BitMapPattern(0x77DD77DD); + + // Boolean Transfer modes. + // http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-196.html#HEADING196-2 + // http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-269.html#HEADING269-2 + // See http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-199.html#HEADING199-76 for color! + int SRC_COPY = 0; + int SRC_OR = 1; + int SRC_XOR = 2; + int SRC_BIC = 3; + int NOT_SRC_COPY = 4; + int NOT_SRC_OR = 5; + int NOT_SRC_XOR = 6; + int NOT_SRC_BIC = 7; + + int PAT_COPY = 8; + int PAT_OR = 9; + int PAT_XOR = 10; + int PAT_BIC = 11; + int NOT_PAT_COPY = 12; + int NOT_PAT_OR = 13; + int NOT_PAT_XOR = 14; + int NOT_PAT_BIC = 15; + + int DITHER_COPY = 64; // Add to src mode for dither + int HILITE = 50; // Add to src or pattern mode for highlight + + // Arithmetic Transfer Modes + // http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-199.html#HEADING199-112 + int BLEND = 32; // dest = source weight/65,535 + destination (1 - weight/65,535) + int ADD_PIN = 33; + int ADD_OVER = 34; + int SUB_PIN = 35; + int TRANSPARENT = 36; + int AD_MAX = 37; + int SUB_OVER = 38; + int AD_MIN = 39; + int GRAYISH_TEXT_OR = 49; +// int MASK = 64; // ?! From Käry's code.. + + /* + * Text face masks. + */ + int TX_BOLD_MASK = 1; + int TX_ITALIC_MASK = 2; + int TX_UNDERLINE_MASK = 4; + int TX_OUTLINE_MASK = 8; + int TX_SHADOWED_MASK = 16; + int TX_CONDENSED_MASK = 32; + int TX_EXTENDED_MASK = 64; +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDrawContext.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDrawContext.java new file mode 100755 index 00000000..92d504d0 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDrawContext.java @@ -0,0 +1,1053 @@ +/* + * 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.pict; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.geom.*; + +/** + * Emulates an Apple QuickDraw rendering context, backed by a Java {@link Graphics2D}. + *

+ * + * @author Harald Kuhr + * @version $Id: QuickDrawContext.java,v 1.0 Oct 3, 2007 1:24:35 AM haraldk Exp$ + */ +// TODO: It would actually be possible to implement a version of this interface +// that wrote opcodes/data to a stream... Or maybe a QDGraphics would be better. +// TODO: Optimize for pensize 1,1? +// TODO: Do we really need the Xxx2D stuff? +// TODO: Support COPY_DITHER +class QuickDrawContext { + + /* + // The useful parts of the QD Graphics Port: + portRect: Rect; {port rectangle} + visRgn: RgnHandle; {visible region} + clipRgn: RgnHandle; {clipping region} + bkPat: Pattern; {background pattern} + fillPat: Pattern; {fill pattern} + pnLoc: Point; {pen location} + pnSize: Point; {pen size} + pnMode: Integer; {pattern mode} + pnPat: Pattern; {pen pattern} + pnVis: Integer; {pen visibility} + txFont: Integer; {font number for text} + txFace: Style; {text's font style} + txMode: Integer; {source mode for text} + txSize: Integer; {font size for text} + spExtra: Fixed; {extra space} + fgColor: LongInt; {foreground color} + bkColor: LongInt; {background color} + colrBit: Integer; {color bit} + .. + picSave: Handle; {picture being saved, used internally} + rgnSave: Handle; {region being saved, used internally} + polySave: Handle; {polygon being saved, used internally} + */ + + /* + // Color Graphics Port; + chExtra: Integer; {added width for nonspace characters} + pnLocHFrac: Integer; {pen fraction} + portRect: Rect; {port rectangle} + visRgn: RgnHandle; {visible region} + clipRgn: RgnHandle; {clipping region} + bkPixPat: PixPatHandle; {background pattern} + rgbFgColor: RGBColor; {requested foreground color} + rgbBkColor: RGBColor; {requested background color} + pnLoc: Point; {pen location} + pnSize: Point; {pen size} + pnMode: Integer; {pattern mode} + pnPixPat: PixPatHandle; {pen pattern} + fillPixPat: PixPatHandle; {fill pattern} + pnVis: Integer; {pen visibility} + txFont: Integer; {font number for text} + txFace: Style; {text's font style} + txMode: Integer; {source mode for text} + txSize: Integer; {font size for text} + spExtra: Fixed; {added width for space characters} + fgColor: LongInt; {actual foreground color} + bkColor: LongInt; {actual background color} + colrBit: Integer; {plane being drawn} + .. + picSave: Handle; {picture being saved, used internally} + rgnSave: Handle; {region being saved, used internally} + polySave: Handle; {polygon being saved, used internally} + */ + private final Graphics2D mGraphics; + + private Pattern mBackground; + + // http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-68.html#HEADING68-0 + // Upon the creation of a graphics port, QuickDraw assigns these initial + // values to the graphics pen: a size of (1,1), a pattern of all-black pixels, + // and the patCopy pattern mode. After changing any of these values, + // you can use the PenNormal procedure to return these initial values to the + // graphics pen. + + // TODO: Consider creating a Pen/PenState class? + private int mPenVisibility = 0; + private Point2D mPenPosition = new Point(); + private Pattern mPenPattern; + private Dimension2D mPenSize = new Dimension(); + private int mPenMode; + + QuickDrawContext(Graphics2D pGraphics) { + if (pGraphics == null) { + throw new IllegalArgumentException("graphics == null"); + } + mGraphics = pGraphics; + + setPenNormal(); + } + + protected void dispose() { + mGraphics.dispose(); + } + + // ClosePicture + public void closePicture() { + dispose(); + } + + // ClipRgn + public void setClipRegion(Shape pClip) { + mGraphics.setClip(pClip); + } + + // Font number (sic), integer + void setTextFont(int fontFamily) { + // ..? + System.err.println("QuickDrawContext.setTextFont"); + } + + // Sets the text's font style (0..255) + void setTextFace(int face) { + // int? + System.err.println("QuickDrawContext.setTextFace"); + } + + void setTextMode(int pSourceMode) { + // ..? + System.err.println("QuickDrawContext.setTextMode"); + } + + public void setTextSize(int pSize) { + mGraphics.setFont(mGraphics.getFont().deriveFont((float) pSize)); + } + + // Numerator (Point), denominator (Point) + void setTextRatio() { + // TODO + System.err.println("QuickDrawContext.setTextRatio"); + } + + // TODO: spExtra added width for space characters + // TODO: chExtra added width for nonspace characters + + public void setOrigin(Point2D pOrigin) { + mGraphics.translate(pOrigin.getX(), pOrigin.getY()); + } + + public void setForeground(Color pColor) { + // TODO: Is this really correct? Or does it depend on pattern mode? + mPenPattern = new BitMapPattern(pColor); + } + + public void setBackground(Color pColor) { + mBackground = new BitMapPattern(pColor); + } + + /* + // Pen management: + // NOTE: The HidePen procedure is called by the OpenRgn, OpenPicture, and OpenPoly routines so that you can create regions, pictures, and polygons without drawing on the screen. + // ShowPen is called by the procedures CloseRgn, ClosePoly, and ClosePicture + GetPenState // All pen state incl. position (PenState type?) + SetPenState + */ + + /** + * HidePen Visibility (decrements visibility by one!) + */ + public void hidePen() { + mPenVisibility--; + } + + /** + * ShowPen Visibility (increments visibility by one!) + */ + public void showPen() { + mPenVisibility++; + } + + /** + * Tells whether pen is visible. + * + * @return {@code true} if pen is visible + */ + private boolean isPenVisible() { + return mPenVisibility >= 0; + } + + /** + * Returns the pen position. + * GetPen + * + * @return the current pen position + */ + public Point2D getPenPosition() { + return (Point2D) mPenPosition.clone(); + } + + /** + * Sets the pen size. + * PenSize + * + * @param pSize the new size + */ + public void setPenSize(Dimension2D pSize) { + mPenSize.setSize(pSize); + mGraphics.setStroke(getStroke(mPenSize)); + } + + /** + * PenMode // Sets pen pattern mode + * + * @param pPenMode the new pen mode + */ + public void setPenMode(int pPenMode) { + // TODO: Handle HILITE (+50) + // TODO: Handle DITHER_COPY (+64) + switch (pPenMode) { + // Boolean source transfer modes + case QuickDraw.SRC_COPY: + case QuickDraw.SRC_OR: + case QuickDraw.SRC_XOR: + case QuickDraw.SRC_BIC: + case QuickDraw.NOT_SRC_COPY: + case QuickDraw.NOT_SRC_OR: + case QuickDraw.NOT_SRC_XOR: + case QuickDraw.NOT_SRC_BIC: + // Boolean pattern transfer modes + case QuickDraw.PAT_COPY: + case QuickDraw.PAT_OR: + case QuickDraw.PAT_XOR: + case QuickDraw.PAT_BIC: + case QuickDraw.NOT_PAT_COPY: + case QuickDraw.NOT_PAT_OR: + case QuickDraw.NOT_PAT_XOR: + case QuickDraw.NOT_PAT_BIC: + // Aritmetic transfer modes + case QuickDraw.BLEND: + case QuickDraw.ADD_PIN: + case QuickDraw.ADD_OVER: + case QuickDraw.SUB_PIN: + case QuickDraw.TRANSPARENT: + case QuickDraw.AD_MAX: + case QuickDraw.SUB_OVER: + case QuickDraw.AD_MIN: + case QuickDraw.GRAYISH_TEXT_OR: + mPenMode = pPenMode; + break; + + default: + throw new IllegalArgumentException("Undefined pen mode: " + pPenMode); + } + } + + /** + * PenPat & PenPixPat // Sets pen bit pattern or pix pattern + * + * @param pPattern the new pattern + */ + public void setPenPattern(final Pattern pPattern) { + mPenPattern = pPattern; + } + + /** + * PenNormal // Reset (except posiotion) + */ + public final void setPenNormal() { + // NOTE: Shold not change pen location + // TODO: What about visibility? Probably not touch + setPenPattern(QuickDraw.BLACK); + setPenSize(new Dimension(1, 1)); + mPenMode = QuickDraw.SRC_COPY; + } + + /* + // Background pattern: + BackPat // Used by the Erase* methods + *BackPixPat + */ + public void setBackgroundPattern(Pattern pPaint) { + mBackground = pPaint; + } + + private Composite getCompositeFor(final int pMode) { + switch (pMode & ~QuickDraw.DITHER_COPY) { + // Boolean source transfer modes + case QuickDraw.SRC_COPY: + return AlphaComposite.Src; // Or, SRC_OVER? + case QuickDraw.SRC_OR: + return AlphaComposite.SrcOver; // Or, DST_OUT? + case QuickDraw.SRC_XOR: + return AlphaComposite.Xor; + case QuickDraw.SRC_BIC: + return AlphaComposite.Clear; + case QuickDraw.NOT_SRC_COPY: + case QuickDraw.NOT_SRC_OR: + case QuickDraw.NOT_SRC_XOR: + case QuickDraw.NOT_SRC_BIC: + throw new UnsupportedOperationException("Not implemented for mode " + pMode); +// return null; + // Boolean pattern transfer modes + case QuickDraw.PAT_COPY: + return AlphaComposite.Src; // Tested + case QuickDraw.PAT_OR: + return AlphaComposite.SrcOver; // Or, DST_OUT? + case QuickDraw.PAT_XOR: + return AlphaComposite.Xor; + case QuickDraw.PAT_BIC: + return AlphaComposite.Clear; + case QuickDraw.NOT_PAT_COPY: + case QuickDraw.NOT_PAT_OR: + case QuickDraw.NOT_PAT_XOR: + case QuickDraw.NOT_PAT_BIC: + throw new UnsupportedOperationException("Not implemented for mode " + pMode); +// return null; + // TODO: Aritmetic transfer modes + + default: + throw new IllegalArgumentException("Unknown pnMode: " + pMode); + } + } + + /** + * Sets up context for line drawing/painting. + */ + protected void setupForPaint() { + mGraphics.setPaint(mPenPattern); + mGraphics.setComposite(getCompositeFor(mPenMode)); + //mGraphics.setStroke(getStroke(mPenSize)); + } + + private Stroke getStroke(final Dimension2D pPenSize) { + // TODO: OPTIMIZE: Only create stroke if changed! + if (pPenSize.getWidth() <= 1.0 && pPenSize.getWidth() == pPenSize.getHeight()) { + return new BasicStroke((float) pPenSize.getWidth()); + } + return new RectangleStroke(new Rectangle2D.Double(0, 0, pPenSize.getWidth(), pPenSize.getHeight())); + } + + /** + * Sets up paint context for fill. + * + * @param pPattern the pattern to use for filling. + */ + protected void setupForFill(final Pattern pPattern) { + mGraphics.setPaint(pPattern); + mGraphics.setComposite(getCompositeFor(QuickDraw.PAT_COPY)); + } + + protected void setupForErase() { + mGraphics.setPaint(mBackground); + mGraphics.setComposite(getCompositeFor(QuickDraw.PAT_COPY)); // TODO: Check spec + } + + protected void setupForInvert() { + // TODO: Setup for invert + mGraphics.setColor(Color.BLACK); + mGraphics.setXORMode(Color.WHITE); + } + + /* + + // Line drawing: + MoveTo // Moves to new pos + Move // distance (MoveTo(h+dh,v+dv)) + LineTo // Draws from current pos to new pos, stores new pos + Line // dinstance (LineTo(h+dh,v+dv)) + */ + + public void moveTo(final double pX, final double pY) { + mPenPosition.setLocation(pX, pY); + } + + public final void moveTo(final Point2D pPosition) { + moveTo(pPosition.getX(), pPosition.getY()); + } + + public final void move(final double pDeltaX, final double pDeltaY) { + moveTo(mPenPosition.getX() + pDeltaX, mPenPosition.getY() + pDeltaY); + } + + public void lineTo(final double pX, final double pY) { + Shape line = new Line2D.Double(mPenPosition.getX(), mPenPosition.getY(), pX, pY); + + // TODO: Add line to current shape if recording... + + if (isPenVisible()) { + // NOTE: Workaround for known Mac JDK bug: Paint, not frame + //mGraphics.setStroke(getStroke(mPenSize)); // Make sure we have correct stroke + paintShape(mGraphics.getStroke().createStrokedShape(line)); + + } + + moveTo(pX, pY); + } + + public final void lineTo(final Point2D pPosition) { + lineTo(pPosition.getX(), pPosition.getY()); + } + + public final void line(final double pDeltaX, final double pDeltaY) { + lineTo(mPenPosition.getX() + pDeltaX, mPenPosition.getY() + pDeltaY); + } + + /* + // Drawing With Color QuickDraw Colors: + * RGBForeColor + * RGBBackColor + * SetCPixel <-- TODO + * FillCRect + * FillCRoundRect + * FillCOval + * FillCArc + * FillCPoly + * FillCRgn + * OpColor // sets the maximum color values for the addPin and subPin arithmetic transfer modes, and the weight color for the blend arithmetic transfer mode. + * HiliteColor // (SKIP?) See http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-199.html#MARKER-9-151 + + // Creating and Managing Rectangles (SKIP? Rely on awt Rectangle): + SetRect + OffsetRect + InsetRect + SectRect + UnionRect + PtInRect + Pt2Rect + PtToAngle + EqualRect + EmptyRect // Reset + */ + + // Drawing Rectangles: + + /** + * FrameRect(r) // outline rect with the size, pattern, and pattern mode of + * the graphics pen. + * + * @param pRectangle the rectangle to frame + */ + public void frameRect(final Rectangle2D pRectangle) { + frameShape(pRectangle); + } + + /** + * PaintRect(r) // fills a rectangle's interior with the pattern of the + * graphics pen, using the pattern mode of the graphics pen. + * + * @param pRectangle the rectangle to paint + */ + public void paintRect(final Rectangle2D pRectangle) { + paintShape(pRectangle); + } + + /** + * FillRect(r, pat) // fills a rectangle's interior with any pattern you + * specify. The procedure transfers the pattern with the patCopy pattern + * mode, which directly copies your requested pattern into the shape. + * + * @param pRectangle the rectangle to fill + * @param pPattern the pattern to use + */ + public void fillRect(final Rectangle2D pRectangle, Pattern pPattern) { + fillShape(pRectangle, pPattern); + } + + /** + * EraseRect(r) // fills the rectangle's interior with the background pattern + * + * @param pRectangle the rectangle to erase + */ + public void eraseRect(final Rectangle2D pRectangle) { + eraseShape(pRectangle); + } + + /** + * InvertRect(r) // reverses the color of all pixels in the rect + * + * @param pRectangle the rectangle to invert + */ + public void invertRect(final Rectangle2D pRectangle) { + invertShape(pRectangle); + } + + // http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-102.html#HEADING102-0 + // Drawing Rounded Rectangles: + private static RoundRectangle2D.Double toRoundRect(final Rectangle2D pRectangle, final int pArcW, final int pArcH) { + return new RoundRectangle2D.Double( + pRectangle.getX(), pRectangle.getY(), + pRectangle.getWidth(), pRectangle.getHeight(), + pArcW, pArcH); + } + + /** + * FrameRoundRect(r,int,int) // outline round rect with the size, pattern, and pattern mode of + * the graphics pen. + * + * @param pRectangle the rectangle to frame + * @param pArcW width of the oval defining the rounded corner. + * @param pArcH height of the oval defining the rounded corner. + */ + public void frameRoundRect(final Rectangle2D pRectangle, int pArcW, int pArcH) { + frameShape(toRoundRect(pRectangle, pArcW, pArcH)); + } + + /** + * PaintRooundRect(r,int,int) // fills a rectangle's interior with the pattern of the + * graphics pen, using the pattern mode of the graphics pen. + * + * @param pRectangle the rectangle to paint + * @param pArcW width of the oval defining the rounded corner. + * @param pArcH height of the oval defining the rounded corner. + */ + public void paintRoundRect(final Rectangle2D pRectangle, int pArcW, int pArcH) { + paintShape(toRoundRect(pRectangle, pArcW, pArcH)); + } + + /** + * FillRoundRect(r,int,int,pat) // fills a rectangle's interior with any pattern you + * specify. The procedure transfers the pattern with the patCopy pattern + * mode, which directly copies your requested pattern into the shape. + * + * @param pRectangle the rectangle to fill + * @param pArcW width of the oval defining the rounded corner. + * @param pArcH height of the oval defining the rounded corner. + * @param pPattern the pattern to use + */ + public void fillRoundRect(final Rectangle2D pRectangle, int pArcW, int pArcH, Pattern pPattern) { + fillShape(toRoundRect(pRectangle, pArcW, pArcH), pPattern); + } + + /** + * EraseRoundRect(r,int,int) // fills the rectangle's interior with the background pattern + * + * @param pRectangle the rectangle to erase + * @param pArcW width of the oval defining the rounded corner. + * @param pArcH height of the oval defining the rounded corner. + */ + public void eraseRoundRect(final Rectangle2D pRectangle, int pArcW, int pArcH) { + eraseShape(toRoundRect(pRectangle, pArcW, pArcH)); + } + + /** + * InvertRoundRect(r,int,int) // reverses the color of all pixels in the rect + * + * @param pRectangle the rectangle to invert + * @param pArcW width of the oval defining the rounded corner. + * @param pArcH height of the oval defining the rounded corner. + */ + public void invertRoundRect(final Rectangle2D pRectangle, int pArcW, int pArcH) { + invertShape(toRoundRect(pRectangle, pArcW, pArcH)); + } + + // Drawing Ovals: + private static Ellipse2D.Double toOval(final Rectangle2D pRectangle) { + Ellipse2D.Double ellipse = new Ellipse2D.Double(); + ellipse.setFrame(pRectangle); + return ellipse; + } + + /** + * FrameOval(r) // outline oval with the size, pattern, and pattern mode of + * the graphics pen. + * + * @param pRectangle the rectangle to frame + */ + public void frameOval(final Rectangle2D pRectangle) { + frameShape(toOval(pRectangle)); + } + + /** + * PaintOval(r) // fills an oval's interior with the pattern of the + * graphics pen, using the pattern mode of the graphics pen. + * + * @param pRectangle the rectangle to paint + */ + public void paintOval(final Rectangle2D pRectangle) { + paintShape(toOval(pRectangle)); + } + + /** + * FillOval(r, pat) // fills an oval's interior with any pattern you + * specify. The procedure transfers the pattern with the patCopy pattern + * mode, which directly copies your requested pattern into the shape. + * + * @param pRectangle the rectangle to fill + * @param pPattern the pattern to use + */ + public void fillOval(final Rectangle2D pRectangle, Pattern pPattern) { + fillShape(toOval(pRectangle), pPattern); + } + + /** + * EraseOval(r) // fills the oval's interior with the background pattern + * + * @param pRectangle the rectangle to erase + */ + public void eraseOval(final Rectangle2D pRectangle) { + eraseShape(toOval(pRectangle)); + } + + /** + * InvertOval(r) // reverses the color of all pixels in the oval + * + * @param pRectangle the rectangle to invert + */ + public void invertOval(final Rectangle2D pRectangle) { + invertShape(toOval(pRectangle)); + } + + // http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-114.html#HEADING114-0 + // NOTE: Differs from Java 2D arcs, in start angle, and rotation + // Drawing Arcs and Wedges: + + /** + * Converts a rectangle to an arc. + * + * @param pRectangle the framing rectangle + * @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java) + * @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs) + * @param pClosed specifies if the arc should be closed + * @return the arc + */ + private static Arc2D.Double toArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle, final boolean pClosed) { + return new Arc2D.Double(pRectangle, 90 - pStartAngle, -pArcAngle, pClosed ? Arc2D.PIE : Arc2D.OPEN); + } + + /** + * FrameArc(r,int,int) // outline arc with the size, pattern, and pattern mode of + * the graphics pen. + * + * @param pRectangle the rectangle to frame + * @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java) + * @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs) + */ + public void frameArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) { + frameShape(toArc(pRectangle, pStartAngle, pArcAngle, false)); + } + + /** + * PaintArc(r,int,int) // fills an arc's interior with the pattern of the + * graphics pen, using the pattern mode of the graphics pen. + * + * @param pRectangle the rectangle to paint + * @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java) + * @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs) + */ + public void paintArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) { + paintShape(toArc(pRectangle, pStartAngle, pArcAngle, true)); + } + + /** + * FillArc(r,int,int, pat) // fills an arc's interior with any pattern you + * specify. The procedure transfers the pattern with the patCopy pattern + * mode, which directly copies your requested pattern into the shape. + * + * @param pRectangle the rectangle to fill + * @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java) + * @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs) + * @param pPattern the pattern to use + */ + public void fillArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle, Pattern pPattern) { + fillShape(toArc(pRectangle, pStartAngle, pArcAngle, true), pPattern); + } + + /** + * EraseArc(r,int,int) // fills the arc's interior with the background pattern + * + * @param pRectangle the rectangle to erase + * @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java) + * @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs) + */ + public void eraseArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) { + eraseShape(toArc(pRectangle, pStartAngle, pArcAngle, true)); + } + + /** + * InvertArc(r,int,int) // reverses the color of all pixels in the arc + * + * @param pRectangle the rectangle to invert + * @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java) + * @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs) + */ + public void invertArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) { + invertShape(toArc(pRectangle, pStartAngle, pArcAngle, true)); + } + + /* + // http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-120.html#HEADING120-0 + // Creating and Managing Polygons: + // - Use Shape? + OpenPoly // Returns a reference to the polygon, used by the paint or other methods + ClosePoly // Close (use LineTo with invisible pen to create) + OffsetPoly + KillPoly // Set internal reference to null + */ + + // Drawing Polygons: + // TODO: What is the Xxx2D equivalent of Polygon!? GeneralPath? + // FramePoly + public void framePoly(final Polygon pPolygon) { + // TODO: The old PICTImageReader does not draw the last connection line, + // unless the start and end point is the same... + // Find out what the spec says. + //if (pPolygon.xpoints[0] == pPolygon.xpoints[pPolygon.npoints - 1] && + // pPolygon.ypoints[0] == pPolygon.ypoints[pPolygon.npoints - 1]) { + //} + frameShape(pPolygon); + } + + // From the source: + // "Four of these procedures--PaintPoly, ErasePoly, InvertPoly, and FillPoly-- + // temporarily convert the polygon into a region to perform their operations" + // PaintPoly + public void paintPoly(final Polygon pPolygon) { + paintShape(pPolygon); + } + + // FillPoly + public void fillPoly(final Polygon pPolygon, final Pattern pPattern) { + fillShape(pPolygon, pPattern); + } + + // ErasePoly + public void erasePoly(final Polygon pPolygon) { + eraseShape(pPolygon); + } + + // InvertPoly + public void invertPoly(final Polygon pPolygon) { + invertShape(pPolygon); + } + + /* + // http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-131.html#HEADING131-0 + // Creating and Managing Regions: + // TODO: Java equiv? Area? + NewRgn // new Region? + OpenRgn // Start collecting region information + CloseRgn + DisposeRgn + CopyRgn + SetEmptyRgn + SetRectRgn + RectRgn + OffsetRgn + InsetRgn + SectRgn + UnionRgn + DiffRgn + XorRgn + PtInRgn + RectInRgn + EqualRgn + EmptyRgn + */ + + // Drawing Regions: + // FrameRgn + public void frameRegion(final Area pArea) { + frameShape(pArea); + } + + // PaintRgn + public void paintRegion(final Area pArea) { + paintShape(pArea); + } + + // FillRgn + public void fillRegion(final Area pArea, final Pattern pPattern) { + fillShape(pArea, pPattern); + } + + // EraseRgn + public void eraseRegion(final Area pArea) { + eraseShape(pArea); + } + + // InvertRgn + public void invertRegion(final Area pArea) { + invertShape(pArea); + } + + // TODO: All other operations can delegate to these! :-) + private void frameShape(final Shape pShape) { + setupForPaint(); + mGraphics.draw(pShape); + } + + private void paintShape(final Shape pShape) { + setupForPaint(); + mGraphics.fill(pShape); + } + + private void fillShape(final Shape pShape, final Pattern pPattern) { + setupForFill(pPattern); + mGraphics.fill(pShape); + } + + private void invertShape(final Shape pShape) { + setupForInvert(); + mGraphics.fill(pShape); + } + + private void eraseShape(final Shape pShape) { + setupForErase(); + mGraphics.fill(pShape); + } + + /* + // Scaling and Mapping Points, Rectangles, Polygons, and Regions: + ScalePt // Use the getXPtCoord/Y from the reader? + MapPt + MapRect + MapRgn + MapPoly + + // Calculating Black-and-White Fills (SKIP?): + SeedFill // MacPaint paint-bucket tool + CalcMask // Calculates where paint would not flow (see above) + + // Copying Images (SKIP?): + */ + /** + * CopyBits. + *

+ * Note that the destination is always {@code this}. + * + * @param pSrcBitmap the source bitmap to copy pixels from + * @param pSrcRect the source rectangle + * @param pDstRect the destination rectangle + * @param pMode the blending mode + * @param pMaskRgn the mask region + */ + public void copyBits(BufferedImage pSrcBitmap, Rectangle pSrcRect, Rectangle pDstRect, int pMode, Shape pMaskRgn) { + mGraphics.setComposite(getCompositeFor(pMode)); + if (pMaskRgn != null) { + setClipRegion(pMaskRgn); + } + + mGraphics.drawImage( + pSrcBitmap, + pDstRect.x, + pDstRect.y, + pDstRect.x + pDstRect.width, + pDstRect.y + pDstRect.height, + pSrcRect.x, + pSrcRect.y, + pSrcRect.x + pSrcRect.width, + pSrcRect.y + pSrcRect.height, + null + ); + } + + /** + * CopyMask + */ + public void copyMask(BufferedImage pSrcBitmap, BufferedImage pMaskBitmap, Rectangle pSrcRect, Rectangle pMaskRect, Rectangle pDstRect, int pSrcCopy, Shape pMaskRgn) { + throw new UnsupportedOperationException("Method copyBits not implemented"); // TODO: Implement + } + + /** + * CopyDeepMask -- available to basic QuickDraw only in System 7, combines the functionality of both CopyBits and CopyMask + */ + public void copyDeepMask(BufferedImage pSrcBitmap, BufferedImage pMaskBitmap, Rectangle pSrcRect, Rectangle pMaskRect, Rectangle pDstRect, int pSrcCopy, Shape pMaskRgn) { + throw new UnsupportedOperationException("Method copyBits not implemented"); // TODO: Implement + } + + /* + // Drawing With the Eight-Color System: + ForeColor // color of the "ink" used to frame, fill, and paint. + BackColor + ColorBit + + // Getting Pattern Resources: + GetPattern + GetIndPattern + + // http://developer.apple.com/documentation/mac/Text/Text-128.html#HEADING128-9 + // Graphics Ports and Text Drawing + + // Setting Text Characteristics: + TextFont // specifies the font to be used. + TextFace // specifies the glyph style. + TextMode // specifies the transfer mode. + TextSize // specifies the font size. + SpaceExtra // specifies the amount of pixels by which to widen or narrow each space character in a range of text. + CharExtra // specifies the amount of pixels by which to widen or narrow each glyph other than the space characters in a range of text (CharExtra). + GetFontInfo + + // Drawing Text: + DrawChar // draws the glyph of a single 1-byte character. + */ + + /** + * DrawString - draws the text of a Pascal string. + * + * @param pString a Pascal string (a string of length less than or equal to 255 chars). + */ + public void drawString(String pString) { + mGraphics.drawString(pString, (float) getPenPosition().getX(), (float) getPenPosition().getY()); + } + + /* + DrawText // draws the glyphs of a sequence of characters. + DrawJustified // draws a sequence of text that is widened or narrowed by a specified number of pixels. + + // Measuring Text: + CharWidth // returns the horizontal extension of a single glyph. + StringWidth // returns the width of a Pascal string. + TextWidth // returns the width of the glyphs of a text segment. + MeasureText // fills an array with an entry for each character identifying the width of each character's glyph as measured from the left side of the entire text segment. + MeasureJustified // fills an array with an entry for each character in a style run identifying the width of each character's glyph as measured from the left side of the text segment. + + // Laying Out a Line of Text: + GetFormatOrder // determines the display order of style runs for a line of text containing multiple style runs with mixed directions + VisibleLength // eliminates trailing spaces from the last style run on the line. + PortionLine // determines how to distribute the total slop value for a line among the style runs on that line. + + // Determining the Caret Position, and Selecting and Highlighting Text: + PixelToChar // converts a pixel location associated with a glyph in a range of text to a byte offset within the style run. + CharToPixel // converts a byte offset to a pixel location. The pixel location is measured from the left edge of the style run. + HiliteText // returns three pairs of offsets marking the endpoints of ranges of text to be highlighted. + + +// Color Constants +ÝwhiteColor =Ý30; +ÝblackColor = 33 +ÝyellowColor = 69; + magentaColor =Ý137; +ÝredColor =Ý205; +ÝcyanColor =Ý273; +ÝgreenColor =Ý341; +ÝblueColor =Ý409; + */ + + // TODO: Simplify! Extract to upper level class + static class RectangleStroke implements Stroke { + + private Shape mShapes[]; + + private boolean repeat = true; + + private AffineTransform mTransform = new AffineTransform(); + + private static final float FLATNESS = 1; + + public RectangleStroke(Shape pShape) { + this(new Shape[]{pShape}); + } + + RectangleStroke(Shape pShapes[]) { + mShapes = new Shape[pShapes.length]; + + for (int i = 0; i < mShapes.length; i++) { + Rectangle2D bounds = pShapes[i].getBounds2D(); + mTransform.setToTranslation(-bounds.getCenterX(), -bounds.getCenterY()); + mShapes[i] = mTransform.createTransformedShape(pShapes[i]); + } + } + + public Shape createStrokedShape(Shape shape) { + GeneralPath result = new GeneralPath(); + + PathIterator it = new FlatteningPathIterator(shape.getPathIterator(null), FLATNESS); + float points[] = new float[6]; + float moveX = 0, moveY = 0; + float lastX = 0, lastY = 0; + float thisX = 0, thisY = 0; + int type = 0; + float next = 0; + int currentShape = 0; + int length = mShapes.length; + + while (currentShape < length && !it.isDone()) { + type = it.currentSegment(points); + switch (type) { + case PathIterator.SEG_MOVETO: + moveX = lastX = points[0]; + moveY = lastY = points[1]; + result.moveTo(moveX, moveY); + next = 0; + break; + + case PathIterator.SEG_CLOSE: + points[0] = moveX; + points[1] = moveY; + // Fall through + + case PathIterator.SEG_LINETO: + thisX = points[0]; + thisY = points[1]; + float dx = thisX - lastX; + float dy = thisY - lastY; + float distance = (float) Math.sqrt(dx * dx + dy * dy); + if (distance >= next) { + float r = 1.0f / distance; + //float angle = (float) Math.atan2(dy, dx); + while (currentShape < length && distance >= next) { + float x = lastX + next * dx * r; + float y = lastY + next * dy * r; + mTransform.setToTranslation(x, y); + //mTransform.rotate(angle); + result.append(mTransform.createTransformedShape(mShapes[currentShape]), false); + next += 1; + currentShape++; + if (repeat) { + currentShape %= length; + } + } + } + next -= distance; + lastX = thisX; + lastY = thisY; + break; + } + it.next(); + } + + return result; + } + + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickTime.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickTime.java new file mode 100755 index 00000000..02991e0b --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickTime.java @@ -0,0 +1,258 @@ +/* + * 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.pict; + +import com.twelvemonkeys.imageio.util.IIOUtil; + +import javax.imageio.IIOException; +import javax.imageio.stream.ImageInputStream; +import java.awt.image.BufferedImage; +import java.io.DataInput; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; + +/** + * QuickTime + * + * @author Harald Kuhr + * @author Matthias Wiesmann (original embedded QuickTime parsing) + * @author last modified by $Author: haraldk$ + * @version $Id: QT.java,v 1.0 Feb 16, 2009 7:20:59 PM haraldk Exp$ + */ +final class QuickTime { + public static final String VENDOR_APPLE = "appl"; + + // TODO: Consider SPI for this in the future, however not very likely + private static final List sDecompressors = Arrays.asList( + new QTBMPDecompressor(), + new QTRAWDecompressor(), + // The GenericDecompressor must be the last in the list + new QTGenericDecompressor() + ); + + /* + Apple compressor id's (vendor 'appl'): + + http://developer.apple.com/DOCUMENTATION/quicktime/Reference/QTRef_Constants/Reference/reference.html + kAnimationCodecType ='rle ' + kAVRJPEGCodecType ='avr ' + kBaseCodecType ='base' + kBMPCodecType ='WRLE' -> BMP without header, SUPPORTED + kCinepakCodecType ='cvid' + kCloudCodecType ='clou' + kCMYKCodecType ='cmyk' -> Is this raw CMYK data? + kComponentVideoCodecType ='yuv2' + kComponentVideoSigned ='yuvu' + kComponentVideoUnsigned ='yuvs' + kDVCNTSCCodecType ='dvc ' + kDVCPALCodecType ='dvcp' + kDVCProNTSCCodecType ='dvpn' + kDVCProPALCodecType ='dvpp' + kFireCodecType ='fire' + kFLCCodecType ='flic' + k48RGBCodecType ='b48r' -> 48 bit (12 bpp) raw color data? + kGIFCodecType ='gif ' -> GIF, should work, but lacks test data + kGraphicsCodecType ='smc ' + kH261CodecType ='h261' + kH263CodecType ='h263' + kIndeo4CodecType ='IV41' + kJPEGCodecType ='jpeg' -> JPEG, SUPPORTED + kMacPaintCodecType ='PNTG' -> Isn't this the PICT format itself? Does that make sense?! ;-) + kMicrosoftVideo1CodecType ='msvc' + kMotionJPEGACodecType ='mjpa' + kMotionJPEGBCodecType ='mjpb' + kMpegYUV420CodecType ='myuv' + kOpenDMLJPEGCodecType ='dmb1' + kPhotoCDCodecType ='kpcd' -> Could potentially use JMagick/JUI plugin + kPlanarRGBCodecType ='8BPS' -> Use (parts of) Photoshop plugin? + kPNGCodecType ='png ' -> PNG, SUPPORTED + kQuickDrawCodecType ='qdrw' -> QD? + kQuickDrawGXCodecType ='qdgx' -> QD? + kRawCodecType ='raw ' -> Raw (A)RGB pixel data + kSGICodecType ='.SGI' + k16GrayCodecType ='b16g' -> Raw 16 bit gray data? + k64ARGBCodecType ='b64a' -> Raw 64 bit (16 bpp) color data? + kSorensonCodecType ='SVQ1' + kSorensonYUV9CodecType ='syv9' + kTargaCodecType ='tga ' -> TGA, maybe create a plugin for that + k32AlphaGrayCodecType ='b32a' -> 16 bit gray + 16 bit alpha raw data? + kTIFFCodecType ='tiff' -> TIFF, SUPPORTED + kVectorCodecType ='path' + kVideoCodecType ='rpza' + kWaterRippleCodecType ='ripl' + kWindowsRawCodecType ='WRAW' -> Raw pixels with reverse byte order ((A)BGR vs (A)RGB)? + kYUV420CodecType ='y420' + */ + + /** + * Gets a decompressor that can decompress the described data. + * + * @param pDescription the image description ({@code 'idsc'} Atom). + * @return a decompressor that can decompress data decribed by the given {@link ImageDesc description}, + * or {@code null} if no decompressor is found + */ + private static QTDecompressor getDecompressor(final ImageDesc pDescription) { + for (QTDecompressor decompressor : sDecompressors) { + if (decompressor.canDecompress(pDescription)) { + return decompressor; + } + } + + return null; + } + + /** + * Decompresses the QuickTime image data from the given stream. + * + * @param pStream the image input stream + * @return a {@link BufferedImage} containing the image data, or {@code null} if no decompressor is capable of + * decompressing the image. + * @throws IOException if an I/O exception occurs during read + */ + public static BufferedImage decompress(final ImageInputStream pStream) throws IOException { + ImageDesc description = ImageDesc.read(pStream); + + if (PICTImageReader.DEBUG) { + System.out.println(description); + } + + QTDecompressor decompressor = getDecompressor(description); + + if (decompressor == null) { + return null; + } + + InputStream streamAdapter = IIOUtil.createStreamAdapter(pStream, description.dataSize); + try { + return decompressor.decompress(description, streamAdapter); + } + finally { + streamAdapter.close(); + } + } + + /** + * Class representing the {@code 'idsc'} QuickTime Atom. + */ + static final class ImageDesc /*extends QTAtom*/ { + private static final int SIZE = 86; + + // 'idsc' Atom size + int size; + + String compressorIdentifer; + short version; + short revision; + String compressorVendor; + + int temporalQuality; + int spatialQuality; + + int width; + int height; + + double horizontalRes; + double verticalRes; + + // Size of image data following 'idsc' + int dataSize; + int frameCount; + + String compressorName; + + short depth; + short colorLUTId; + + byte[] extraDesc; + + private ImageDesc() {} + + public static ImageDesc read(final DataInput pStream) throws IOException { + // The following looks like the 'idsc' Atom (as described in the QuickTime File Format) + ImageDesc description = new ImageDesc(); + + description.size = pStream.readInt(); + description.compressorIdentifer = PICTUtil.readIdString(pStream); + + pStream.skipBytes(4); // Reserved, should be 0 + pStream.skipBytes(2); // Reserved, should be 0 + pStream.skipBytes(2); // Reserved, should be 0 + + description.version = pStream.readShort(); // Major version, 0 if not applicable + description.revision = pStream.readShort(); // Minor version, 0 if not applicable + description.compressorVendor = PICTUtil.readIdString(pStream); + + description.temporalQuality = pStream.readInt(); // Temporal quality, 0 means "no temporal compression" + description.spatialQuality = pStream.readInt(); // Spatial quality, 0x0000 0200 is codecNormalQuality + + description.width = pStream.readShort(); // Width (short) + description.height = pStream.readShort(); // Height (short) + + description.horizontalRes = PICTUtil.readFixedPoint(pStream); // Horizontal resolution, FP, 0x0048 0000 means 72 DPI + description.verticalRes = PICTUtil.readFixedPoint(pStream); // Vertical resolution, FP, 0x0048 0000 means 72 DPI + + // TODO: Handle 0 data size as unknown + description.dataSize = pStream.readInt(); // Data size, may be 0, if unknown + description.frameCount = pStream.readShort(); // Frame count + + description.compressorName = PICTUtil.readStr31(pStream); // Compresor name, 32 byte null-terminated Pascal String + description.depth = pStream.readShort(); // Image bit depth + description.colorLUTId = pStream.readShort(); // Color Lookup Table Id, -1 means none + + int extraDescSize = description.size - ImageDesc.SIZE; + if (extraDescSize < 0) { + throw new IIOException("Negative array size in 'idsc' Atom: " + extraDescSize); + } + description.extraDesc = new byte[extraDescSize]; + pStream.readFully(description.extraDesc); + + return description; + } + + @Override + public String toString() { + return String.format("'idsc', size: %s, id: '%s', ver: %s, rev: %s, vendor: '%s', " + +// "tempQ: %s, spatQ: %s, " + + "w: %d, h: %d, " + +// "horiz: %s, vert: %s, " + + "data-size: %d, frame-count: %s, " + + "name: '%s', depth: %d, lut: %s, extra: %d", + size, compressorIdentifer, version, revision, compressorVendor, +// temporalQuality, spatialQuality, + width, height, +// horizontalRes, verticalRes, + dataSize, frameCount, + compressorName, depth, + colorLUTId, extraDesc != null ? extraDesc.length : 0 + ); + } + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/RGBColor.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/RGBColor.java new file mode 100755 index 00000000..8ee599e3 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/RGBColor.java @@ -0,0 +1,45 @@ +/* + * 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.pict; + +import java.awt.*; + +/** + * RGBColor + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: RGBColor.java,v 1.0 Mar 2, 2009 10:39:24 AM haraldk Exp$ + */ +class RGBColor extends Color { + // TODO: Is the 16 bit resolution ever signigicant? + public RGBColor(short r, short g, short b) { + super((r & 0xffff) / (float) 0xffff, (g & 0xffff) / (float) 0xffff, (b & 0xffff) / (float) 0xffff); + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/TestPICTClippingApp.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/TestPICTClippingApp.java new file mode 100755 index 00000000..a614d20c --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/TestPICTClippingApp.java @@ -0,0 +1,191 @@ +package com.twelvemonkeys.imageio.plugins.pict; + +import com.twelvemonkeys.image.BufferedImageIcon; +import com.twelvemonkeys.image.ImageUtil; +import com.twelvemonkeys.net.MIMEUtil; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.ImageReadParam; +import javax.imageio.stream.ImageInputStream; +import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.List; + +/** + * TestPICTClippingApp + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: TestPICTClippingApp.java,v 1.0 Feb 16, 2009 3:05:16 PM haraldk Exp$ + */ +public class TestPICTClippingApp { + public static void main(final String[] pArgs) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch (Exception ignore) { + } + + JFrame frame = new JFrame("PICTClipping test"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JLabel dropZone = new JLabel("Drop images here", JLabel.CENTER) { + @Override + public Dimension getPreferredSize() { + return new Dimension(320, 200); + } + }; + + dropZone.setTransferHandler(new ImageDropHandler(dropZone)); + frame.add(dropZone); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + }); + } + + private static class ImageDropHandler extends TransferHandler { + private final JLabel mLabel; + + public ImageDropHandler(JLabel pLabel) { + super(null); + mLabel = pLabel; + } + + private DataFlavor getSupportedFlavor(final DataFlavor[] transferFlavors) { + for (DataFlavor flavor : transferFlavors) { + String type = MIMEUtil.bareMIME(flavor.getMimeType()); + if (InputStream.class.isAssignableFrom(flavor.getDefaultRepresentationClass()) && ImageIO.getImageReadersByMIMEType(type).hasNext()) { + return flavor; + } + else if (flavor.equals(DataFlavor.javaFileListFlavor)) { + return flavor; + } + } + + for (DataFlavor flavor : transferFlavors) { + System.err.printf("flavor: %s%n", flavor); + } + + return null; + } + + @Override + public boolean canImport(final JComponent comp, final DataFlavor[] transferFlavors) { + return getSupportedFlavor(transferFlavors) != null; + } + + @Override + public boolean importData(JComponent comp, Transferable t) { + DataFlavor[] flavors = t.getTransferDataFlavors(); + DataFlavor flavor = getSupportedFlavor(flavors); + if (flavor != null) { + try { + InputStream input; + if (DataFlavor.javaFileListFlavor.equals(flavor)) { + List files = (List) t.getTransferData(flavor); + if (files.isEmpty()) { + return false; + } + else { + input = new FileInputStream((File) files.get(0)); + } + } + else { + Object data = t.getTransferData(flavor); + input = (InputStream) data; + } + + ImageInputStream stream = ImageIO.createImageInputStream(input); + Iterator readers = ImageIO.getImageReaders(stream); + + if (!readers.hasNext()) { + String mimeType = MIMEUtil.bareMIME(flavor.getMimeType()); + System.out.printf("Getting reader by MIME type (%s)...%n", mimeType); + readers = ImageIO.getImageReadersByMIMEType(mimeType); + } + + if (readers.hasNext()) { + ImageReader reader = readers.next(); + + reader.setInput(stream); + + final int maxDimension = 200; + int w = reader.getWidth(0); + int h = reader.getHeight(0); + + ImageReadParam param = null; + if (w > maxDimension && h > maxDimension) { + int sub = (int) Math.ceil(Math.min(w, h) / (double) maxDimension) / 3; + if (sub > 1) { + param = reader.getDefaultReadParam(); + param.setSourceSubsampling(sub, sub, 0, 0); + } + } + + System.out.printf("Reading %s format%s... ", reader.getFormatName(), (param != null ? ", sampling every " + param.getSourceXSubsampling() + "th pixel" : "")); + final BufferedImage image = reader.read(0, param); + System.out.printf("Done (%dx%d).%n", image.getWidth(), image.getHeight()); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + System.out.print("Scaling image... "); + BufferedImage scaled = box(image, maxDimension); + System.out.printf("Done (%dx%d).%n", scaled.getWidth(), scaled.getHeight()); + mLabel.setIcon(new BufferedImageIcon(scaled)); + } + }); + + return true; + } + else { + System.err.println("No reader found!"); + } + + } + catch (UnsupportedFlavorException ignore) { + ignore.printStackTrace(); + } + catch (IOException ignore) { + ignore.printStackTrace(); + } + catch (RuntimeException e) { + e.printStackTrace(); + throw e; + } + } + + return false; + } + + private BufferedImage box(final BufferedImage pImage, final int pMaxDimension) { + if (pImage.getWidth() > pMaxDimension || pImage.getHeight() > pMaxDimension) { + int w, h; + + if (pImage.getWidth() > pImage.getHeight()) { + w = pMaxDimension; + h = (int) Math.round(w / (pImage.getWidth() / (double) pImage.getHeight())); + } + else { + h = pMaxDimension; + w = (int) Math.round(h * (pImage.getWidth() / (double) pImage.getHeight())); + } + + return ImageUtil.createResampled(pImage, w, h, Image.SCALE_DEFAULT); + } + return pImage; + } + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/TestPICTEagerDetect.java b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/TestPICTEagerDetect.java new file mode 100755 index 00000000..0a1630c6 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/TestPICTEagerDetect.java @@ -0,0 +1,26 @@ +package com.twelvemonkeys.imageio.plugins.pict; + +import javax.imageio.ImageIO; +import java.io.File; +import java.io.IOException; + +/** + * TestPICTEagerDetect + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: TestPICTEagerDetect.java,v 1.0 Aug 6, 2009 2:59:41 PM haraldk Exp$ + */ +public class TestPICTEagerDetect { + public static void main(final String[] pArgs) throws IOException { + PICTImageReaderSpi provider = new PICTImageReaderSpi(); + + if (pArgs.length == 0) { + System.exit(1); + } + for (String arg : pArgs) { + boolean canDecode = provider.canDecodeInput(ImageIO.createImageInputStream(new File(arg))); + System.err.printf("canDecode %s: %s%n", arg, canDecode); + } + } +} diff --git a/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/plpictdec.cpp b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/plpictdec.cpp new file mode 100755 index 00000000..5169f71c --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/plpictdec.cpp @@ -0,0 +1,1272 @@ +/* +http://www.koders.com/cpp/fidEBFA7988680615CF80EF7DA28116F3E423B56E7A.aspx +http://www.paintlib.de/paintlib/copyright.html +/-------------------------------------------------------------------- +| +| $Id: plpictdec.cpp,v 1.14 2004/10/02 22:23:12 uzadow Exp $ +| Macintosh pict Decoder Class +| +| This class decodes macintosh PICT files with 1,2,4,8,16 and 32 +| bits per pixel as well as PICT/JPEG. If an alpha channel is +| present in a 32-bit-PICT, it is decoded as well. +| The PICT format is a general picture file format and can +| contain a lot of other elements besides bitmaps. These elements +| are ignored. +| +| Copyright (c) 1996-2002 Ulrich von Zadow +| +\-------------------------------------------------------------------- +*/ + +#include "config.h" +#include "plpictdec.h" +#ifdef PL_SUPPORT_JPEG +#include "pljpegdec.h" +#endif +#include "ploptable.h" +#include "plexcept.h" + +#include + +PLPictDecoder::PLPictDecoder + ( PLJPEGDecoder * pJPEGDecoder + ) + : PLPicDecoder(), + m_pJPEGDecoder (pJPEGDecoder) + // Creates a decoder +{ +} + + +PLPictDecoder::~PLPictDecoder + () +{ +} + + +void PLPictDecoder::Open (PLDataSource * pDataSrc) +{ + Trace (2, "Decoding mac pict.\n"); + + // Skip empty 512 byte header. + pDataSrc->Skip (512); + + // Read PICT header + int Version; + readHeader (pDataSrc, Version); + interpretOpcodes (pDataSrc, Version); + +} + + +//! Fills the bitmap with the image. +void PLPictDecoder::GetImage (PLBmpBase & Bmp) +{ + switch (m_PictType) + { + case op9a: + DecodeOp9a (&Bmp, m_pDataSrc); + break; + case jpeg: + DecodeJPEG (&Bmp, m_pDataSrc); + break; + case pixmap: + DecodePixmap (&Bmp, m_pDataSrc); + break; + case bitmap: + DecodeBitmap (&Bmp, m_pDataSrc); + break; + default: + PLASSERT (false); + } + m_PictType = none; +} + + +void PLPictDecoder::readHeader + ( PLDataSource * pDataSrc, + int& Version + ) + // Decodes header and version information. + // Performs checks to make sure the data is really a pict file. +{ + PLBYTE ch; + PLWORD PicSize; // Version 1 picture size. Ignored in version 2. + char sz[256]; + MacRect Frame; + + PicSize = ReadMWord (pDataSrc); + + readRect (&Frame, pDataSrc); + + while ((ch = ReadByte(pDataSrc)) == 0); + if (ch != 0x11) + raiseError (PL_ERRWRONG_SIGNATURE, + "Error decoding pict: Version number missing."); + + switch (ReadByte(pDataSrc)) + { + case 1: + Version = 1; + break; + case 2: + if (ReadByte(pDataSrc) != 0xff) + raiseError (PL_ERRWRONG_SIGNATURE, + "Illegal version number."); + Version = 2; + break; + default: + raiseError (PL_ERRWRONG_SIGNATURE, + "Illegal version number."); + } + + sprintf (sz, "PICT version %d found.\n", Version); + Trace (2, sz); +} + + +void PLPictDecoder::interpretOpcodes + ( PLDataSource * pDataSrc, + int& Version + ) + // This is the main decoder loop. The functions reads opcodes, + // skips some, and hands the rest to opcode-specific functions. + // It stops decoding after the first opcode containing bitmapped + // data. +{ + PLWORD Opcode; + char sz[256]; + + bool bDone = false; + + while (!bDone) + { + Opcode = readOpcode(Version, pDataSrc); + + if (Opcode == 0xFF || Opcode == 0xFFFF) + { + bDone = true; + Trace (2, "Opcode: End of pict.\n"); + raiseError (PL_ERRFORMAT_NOT_SUPPORTED, + "PICT contained only vector data!\n"); + } + else if (Opcode < 0xa2) + { + if (!strcmp(optable[Opcode].name, "reserved")) + sprintf (sz, "Opcode: reserved=0x%x\n", Opcode); + else + sprintf (sz, "Opcode: %s\n", optable[Opcode].name); + Trace (2, sz); + + switch (Opcode) + { + case 0x01: // Clip + clip (pDataSrc); + break; + case 0x12: + case 0x13: + case 0x14: + pixPat (pDataSrc); + break; + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + skipPolyOrRegion (pDataSrc); + break; + case 0x90: + case 0x98: + bitsRect (pDataSrc); + bDone = true; + break; + case 0x91: + case 0x99: + bitsRegion (pDataSrc); + bDone = true; + break; + case 0x9a: + opcode9a (pDataSrc); + bDone = true; + break; + case 0xa1: + longComment (pDataSrc); + break; + default: + // No function => skip to next Opcode + if (optable[Opcode].len == WORD_LEN) + pDataSrc->Skip(ReadMWord(pDataSrc)); + else + pDataSrc->Skip(optable[Opcode].len); + } + } + else if (Opcode == 0xc00) + { + Trace (2, "Opcode: Header.\n"); + headerOp (pDataSrc); + } + else if (Opcode == 0x8200) + { + Trace (2, "Opcode: JPEG.\n"); + jpegOp (pDataSrc); + bDone = true; + } + else if (Opcode >= 0xa2 && Opcode <= 0xaf) + { + sprintf (sz, "Opcode: reserved 0x%x.\n", Opcode); + Trace (2, sz); + pDataSrc->Skip(ReadMWord(pDataSrc)); + } + else if ((Opcode >= 0xb0 && Opcode <= 0xcf) || + (Opcode >= 0x8000 && Opcode <= 0x80ff)) + { + // just a reserved Opcode, no data + sprintf (sz, "Opcode: reserved 0x%x.\n", Opcode); + Trace (2, sz); + } + else if ((Opcode >= 0xd0 && Opcode <= 0xfe) || + Opcode >= 8100) + { + sprintf (sz, "Opcode: reserved 0x%x.\n", Opcode); + Trace (2, sz); + pDataSrc->Skip(ReadMLong(pDataSrc)); + } + else if (Opcode >= 0x100 && Opcode <= 0x7fff) + { + sprintf (sz, "Opcode: reserved 0x%x.\n", Opcode); + Trace (2, sz); + pDataSrc->Skip((Opcode >> 7) & 255); + } + else + { + char sz[256]; + sprintf (sz, "Can't handle Opcode %x.\n", Opcode); + raiseError (PL_ERRFORMAT_UNKNOWN, sz); + } + } +} + + +PLWORD PLPictDecoder::readOpcode + ( int Version, + PLDataSource * pDataSrc + ) + // moves to an even byte position in the file and returns the + // opcode found. +{ + if (Version == 2) + pDataSrc->AlignToWord(); + + if (Version == 1) + return ReadByte (pDataSrc); + else + return ReadMWord (pDataSrc); +} + + +///////////////////////////////////////////////////////////////////// +// Opcode functions + +void PLPictDecoder::clip + ( PLDataSource * pDataSrc + ) + // skips clipping rectangle or region. +{ + MacRect ClipRect; + + PLWORD len = ReadMWord(pDataSrc); + + if (len == 0x000a) + { /* null rgn */ + readRect(&ClipRect, pDataSrc); + } + else + pDataSrc->Skip(len - 2); +} + +void PLPictDecoder::pixPat + ( PLDataSource * pDataSrc + ) + // skips pattern definition. +{ + PLWORD PatType; + PLWORD rowBytes; + MacpixMap p; + PLWORD NumColors; + + PatType = ReadMWord(pDataSrc); + + switch (PatType) + { + case 2: + pDataSrc->Skip(8); + pDataSrc->Skip(5); + break; + case 1: + { + pDataSrc->Skip(8); + rowBytes = ReadMWord(pDataSrc); + readRect(&p.Bounds, pDataSrc); + readPixmap(&p, pDataSrc); + + PLPixel32 CT[256]; + readColourTable(&NumColors, pDataSrc, CT); + skipBits(&p.Bounds, rowBytes, p.pixelSize, pDataSrc); + } + break; + default: + raiseError (PL_ERRFORMAT_UNKNOWN, + "Unknown pattern type in pixPat."); + } +} + +void PLPictDecoder::skipPolyOrRegion + ( PLDataSource * pDataSrc + ) +{ + Trace (3, "Skipping polygon or region.\n"); + pDataSrc->Skip (ReadMWord (pDataSrc) - 2); +} + +void PLPictDecoder::bitsRect + ( PLDataSource * pDataSrc + ) + // Bitmap/pixmap data clipped by a rectangle. +{ + m_rowBytes = ReadMWord(pDataSrc); // Bytes per row in source when + // uncompressed. + + m_bIsRegion = false; + if (m_rowBytes & 0x8000) + doPixmap (pDataSrc); + else + doBitmap (pDataSrc); +} + +void PLPictDecoder::bitsRegion + ( PLDataSource * pDataSrc + ) + // Bitmap/pixmap data clipped by a region. +{ + m_rowBytes = ReadMWord(pDataSrc); // Bytes per row in source when + // uncompressed. + m_bIsRegion = true; + if (m_rowBytes & 0x8000) + doPixmap (pDataSrc); + else + doBitmap (pDataSrc); +} + +void PLPictDecoder::opcode9a + ( PLDataSource * pDataSrc + ) + // DirectBitsRect. +{ + pDataSrc->Skip(4); // Skip fake len and fake EOF. + ReadMWord(pDataSrc); // bogus row bytes. + + // Read in the PixMap fields. + readRect(&m_PixMap.Bounds, pDataSrc); + readPixmap (&m_PixMap, pDataSrc); + + // Ignore source & destination rectangle as well as transfer mode. + MacRect TempRect; + readRect (&TempRect, pDataSrc); + readRect (&TempRect, pDataSrc); + PLWORD mode = ReadMWord(pDataSrc); + + // Create empty DIB + setBmpInfo (m_PixMap); + m_PictType=op9a; +} + +void PLPictDecoder::DecodeOp9a + ( PLBmpBase * pBmp, + PLDataSource * pDataSrc + ) +{ + // Do the actual unpacking. + switch (m_PixMap.pixelSize) + { + case 32: + unpack32bits (&m_PixMap.Bounds, 0, m_PixMap.cmpCount, pBmp, pDataSrc); + break; + case 8: + unpack8bits (&m_PixMap.Bounds, 0, pBmp, pDataSrc); + break; + default: + unpackbits (&m_PixMap.Bounds, 0, m_PixMap.pixelSize, pBmp, pDataSrc); + } +} + +void PLPictDecoder::longComment + ( PLDataSource * pDataSrc + ) +{ + PLWORD type; + PLWORD len; + + type = ReadMWord(pDataSrc); + len = ReadMWord(pDataSrc); + if (len > 0) + pDataSrc->Skip (len); +} + +void PLPictDecoder::headerOp + ( PLDataSource * pDataSrc + ) +{ + int Version = ReadMWord (pDataSrc); + m_Resolution.x = ReadMLong(pDataSrc); + m_Resolution.y = ReadMLong(pDataSrc); + MacRect Dummy; + readRect (&Dummy, pDataSrc); + ReadMWord (pDataSrc); +} + +void PLPictDecoder::jpegOp + ( PLDataSource * pDataSrc + ) + // Invoke the JPEG decoder for this PICT. +{ + long OpLen = ReadMLong(pDataSrc); + bool bFound = false; + int i = 0; + + // skip to JPEG header. + while (!bFound && i < OpLen) + { + PLBYTE * pData = pDataSrc->GetBufferPtr (3); + if (pData[0] == 0xFF && pData[1] == 0xD8 && pData[2] == 0xFF) + bFound = true; + else + { + ReadByte(pDataSrc); + i++; + } + } + if (bFound) + // Pass the data to the JPEG decoder. + if (m_pJPEGDecoder) +#ifdef PL_SUPPORT_JPEG + { + m_pJPEGDecoder->Open (pDataSrc); + SetBmpInfo (*m_pJPEGDecoder); + m_PictType = jpeg; + } +#else + raiseError (PL_ERRFORMAT_NOT_SUPPORTED, + "Library not compiled for PICT/JPEG."); +#endif + else + raiseError (PL_ERRFORMAT_NOT_SUPPORTED, + "Library not compiled for PICT/JPEG."); + else + raiseError (PL_ERRFORMAT_NOT_SUPPORTED, + "PICT file contains unrecognized quicktime data.\n"); +} + +void PLPictDecoder::DecodeJPEG + ( PLBmpBase * pBmp, + PLDataSource * pDataSrc + ) +{ +#ifdef PL_SUPPORT_JPEG + m_pJPEGDecoder->GetImage(*pBmp); +#else + raiseError (PL_ERRFORMAT_NOT_SUPPORTED, + "Library not compiled for PICT/JPEG."); +#endif +} + + +///////////////////////////////////////////////////////////////////// +// Bitmap & Pixmap functions + +void PLPictDecoder::setBmpInfo + ( MacpixMap PixMap + ) +{ + + PLPixelFormat pf; + if (PixMap.pixelSize > 8) + if (PixMap.cmpCount == 4) + pf = PLPixelFormat::A8R8G8B8; + else + pf = PLPixelFormat::X8R8G8B8; + else + pf = PLPixelFormat::I8; + + SetBmpInfo (PLPoint (PixMap.Bounds.right - PixMap.Bounds.left, + PixMap.Bounds.bottom - PixMap.Bounds.top), + PLPoint (PixMap.hRes, PixMap.vRes), pf); +} + + +void PLPictDecoder::doBitmap + ( PLDataSource * pDataSrc + ) + // Decode version 1 bitmap: 1 bpp. +{ + MacRect SrcRect; + MacRect DstRect; + PLWORD width; // Width in pixels + PLWORD height; // Height in pixels + + Trace (2, "Reading version 1 bitmap.\n"); + + readRect(&m_Bounds, pDataSrc); + dumpRect (" Bounds", &m_Bounds); + readRect(&SrcRect, pDataSrc); + readRect(&DstRect, pDataSrc); + + width = m_Bounds.right - m_Bounds.left; + height = m_Bounds.bottom - m_Bounds.top; + + SetBmpInfo (PLPoint (width, height), PLPoint (0,0), PLPixelFormat::I8); + m_PictType = bitmap; +} + +void PLPictDecoder::DecodeBitmap + ( PLBmpBase * pBmp, + PLDataSource * pDataSrc + ) +{ + PLWORD mode = ReadMWord(pDataSrc); + + if (m_bIsRegion) + skipPolyOrRegion (pDataSrc); + + pBmp->SetPaletteEntry (0, 0, 0, 0, 255); + pBmp->SetPaletteEntry (1, 255, 255, 255, 255); + unpackbits (&m_Bounds, m_rowBytes, 1, pBmp, pDataSrc); +} + + +void PLPictDecoder::doPixmap + ( PLDataSource * pDataSrc + ) + // Decode version 2 pixmap +{ + readRect(&m_PixMap.Bounds, pDataSrc); + readPixmap(&m_PixMap, pDataSrc); + + setBmpInfo (m_PixMap); + m_PictType = pixmap; +} + +void PLPictDecoder::DecodePixmap + ( PLBmpBase * pBmp, + PLDataSource * pDataSrc + ) +{ + // Read mac colour table into windows palette. + PLWORD NumColors; // Palette size. + PLPixel32 Pal[256]; + + readColourTable (&NumColors, pDataSrc, Pal); + if (pBmp->GetBitsPerPixel() == 8) + pBmp->SetPalette (Pal); + + // Ignore source & destination rectangle as well as transfer mode. + MacRect TempRect; + readRect (&TempRect, pDataSrc); + readRect (&TempRect, pDataSrc); + PLWORD mode = ReadMWord(pDataSrc); + + if (m_bIsRegion) + skipPolyOrRegion (pDataSrc); + + switch (m_PixMap.pixelSize) + { + case 32: + unpack32bits (&m_PixMap.Bounds, m_rowBytes, m_PixMap.cmpCount, pBmp, pDataSrc); + break; + case 8: + unpack8bits (&m_PixMap.Bounds, m_rowBytes, pBmp, pDataSrc); + break; + default: + unpackbits (&m_PixMap.Bounds, m_rowBytes, m_PixMap.pixelSize, + pBmp, pDataSrc); + } +} + +void PLPictDecoder::unpack32bits + ( MacRect* pBounds, + PLWORD rowBytes, + int NumBitPlanes, // 3 if RGB, 4 if RGBA + PLBmpBase * pBmp, + PLDataSource * pDataSrc + ) + // This routine decompresses BitsRects with a packType of 4 (and + // 32 bits per pixel). In this format, each line is separated + // into 8-bit-bitplanes and then compressed via RLE. To decode, + // the routine decompresses each line & then juggles the bytes + // around to get pixel-oriented data. +{ + int i,j; + PLWORD BytesPerRow; // bytes per row when uncompressed. + PLPixel32 * pDestLine; + + PLBYTE * pLinebuf; // Temporary buffer for line data. In + // this buffer, pixels are uncompressed + // but still plane-oriented. + PLPixel32 ** pLineArray = pBmp->GetLineArray32(); + + + int Height = pBounds->bottom - pBounds->top; + int Width = pBounds->right - pBounds->left; + + BytesPerRow = Width*NumBitPlanes; + + if (rowBytes == 0) + rowBytes = Width*4; + + // Allocate temporary line buffer. + pLinebuf = new PLBYTE [BytesPerRow]; + + try + { + for (i = 0; i < Height; i++) + { // for each line do... + int linelen; // length of source line in bytes. + if (rowBytes > 250) + linelen = ReadMWord(pDataSrc); + else + linelen = ReadByte(pDataSrc); + + PLBYTE * pBuf = unpackPictRow (pLinebuf, pDataSrc, Width, rowBytes, linelen); + + // Convert plane-oriented data into pixel-oriented data & + // copy into destination bitmap. + pDestLine = pLineArray[i]; + + if (NumBitPlanes == 3) + for (j = 0; j < Width; j++) + { // For each pixel in line... + pDestLine->SetB (*(pBuf+Width*2)); // Blue + pDestLine->SetG (*(pBuf+Width)); // Green + pDestLine->SetR (*pBuf); // Red + pDestLine->SetA (0xFF); + pDestLine++; + pBuf++; + } + else + for (j = 0; j < Width; j++) + { // For each pixel in line... + pDestLine->SetB (*(pBuf+Width*3)); // Blue + pDestLine->SetG (*(pBuf+Width*2)); // Green + pDestLine->SetR (*(pBuf+Width)); // Red + pDestLine->SetA (*pBuf); + pDestLine++; + pBuf++; + } + } + } + catch (PLTextException) + { + delete [] pLinebuf; + throw; + } + catch(...) + { + delete [] pLinebuf; + throw; + } + delete [] pLinebuf; +} + + +void PLPictDecoder::unpack8bits + ( MacRect* pBounds, + PLWORD rowBytes, + PLBmpBase * pBmp, + PLDataSource * pDataSrc + ) + // Decompression routine for 8 bpp. rowBytes is the number of + // bytes each source row would take if it were uncompressed. + // This _isn't_ equal to the number of pixels in the row - it + // seems apple pads the data to a word boundary and then + // compresses it. Of course, we have to decompress the excess + // data and then throw it away. +{ + int i; + PLBYTE ** pLineArray = pBmp->GetLineArray(); + + int Height = pBounds->bottom - pBounds->top; + int Width = pBounds->right - pBounds->left; + + // High bit of rowBytes is flag. + rowBytes &= 0x7fff; + + if (rowBytes == 0) + rowBytes = Width; + + PLBYTE * pLineBuf = new PLBYTE [rowBytes]; + + try + { + for (i = 0; i < Height; i++) + { + int linelen; // length of source line in bytes. + if (rowBytes > 250) + linelen = ReadMWord(pDataSrc); + else + linelen = ReadByte(pDataSrc); + PLBYTE * pRawLine = unpackPictRow (pLineBuf, pDataSrc, Width, rowBytes, linelen); + memcpy (pLineArray[i], pRawLine, Width); + } + } + catch (PLTextException) + { + delete [] pLineBuf; + throw; + } + delete [] pLineBuf; +} + + +void PLPictDecoder::unpackbits + ( MacRect* pBounds, + PLWORD rowBytes, + int pixelSize, // Source bits per pixel. + PLBmpBase * pBmp, + PLDataSource * pDataSrc + ) + // Decompression routine for everything but 8 & 32 bpp. This + // routine is slower than the two routines above since it has to + // deal with a lot of special cases :-(. + // It's also a bit chaotic because of these special cases... + // unpack8bits is basically a dumber version of unpackbits. +{ + PLBYTE * pSrcLine; // Pointer to source line in file. + int i,j,k; + PLWORD pixwidth; // bytes per row when uncompressed. + int linelen; // length of source line in bytes. + int pkpixsize; + PLBYTE * pDestLine; + PLBYTE FlagCounter; + int len; + int PixelPerRLEUnit; + PLBYTE * pLineBuf; + PLBYTE * pBuf; + PLBYTE ** pLineArray = pBmp->GetLineArray(); + + int Height = pBounds->bottom - pBounds->top; + int Width = pBounds->right - pBounds->left; + + // High bit of rowBytes is flag. + if (pixelSize <= 8) + rowBytes &= 0x7fff; + + pixwidth = Width; + pkpixsize = 1; // RLE unit: one byte for everything... + if (pixelSize == 16) // ...except 16 bpp. + { + pkpixsize = 2; + pixwidth *= 2; + } + + if (rowBytes == 0) + rowBytes = pixwidth; + + try + { + // I allocate the temporary line buffer here. I allocate too + // much memory to compensate for sloppy (& hence fast) + // decompression. + switch (pixelSize) + { + case 1: + PixelPerRLEUnit = 8; + pLineBuf = new PLBYTE [(rowBytes+1) * 32]; + break; + case 2: + PixelPerRLEUnit = 4; + pLineBuf = new PLBYTE [(rowBytes+1) * 16]; + break; + case 4: + PixelPerRLEUnit = 2; + pLineBuf = new PLBYTE [(rowBytes+1) * 8]; + break; + case 8: + PixelPerRLEUnit = 1; + pLineBuf = new PLBYTE [rowBytes * 4]; + break; + case 16: + PixelPerRLEUnit = 1; + pLineBuf = new PLBYTE [rowBytes * 2 + 4]; + break; + default: + char sz[256]; + sprintf (sz, + "Illegal bpp value in unpackbits: %d\n", + pixelSize); + raiseError (PL_ERRFORMAT_UNKNOWN, sz); + } + + if (rowBytes < 8) + { // ah-ha! The bits aren't actually packed. This will be easy. + for (i = 0; i < Height; i++) + { + pDestLine = pLineArray[i]; + pSrcLine = pDataSrc->ReadNBytes (rowBytes); + if (pixelSize == 16) + expandBuf(pDestLine, pSrcLine, Width, pixelSize); + else + expandBuf8(pDestLine, pSrcLine, Width, pixelSize); + } + } + else + { + for (i = 0; i < Height; i++) + { // For each line do... + if (rowBytes > 250) + linelen = ReadMWord(pDataSrc); + else + linelen = ReadByte(pDataSrc); + + pSrcLine = pDataSrc->ReadNBytes(linelen); + pBuf = pLineBuf; + + // Unpack RLE. The data is packed bytewise - except for + // 16 bpp data, which is packed per pixel :-(. + for (j = 0; j < linelen; ) + { + FlagCounter = pSrcLine[j]; + if (FlagCounter & 0x80) + { + if (FlagCounter == 0x80) + // Special case: repeat value of 0. + // Apple sais ignore. + j++; + else + { // Packed data. + len = ((FlagCounter ^ 255) & 255) + 2; + + // This is slow for some formats... + if (pixelSize == 16) + { + expandBuf (pBuf, pSrcLine+j+1, 1, pixelSize); + for (k = 1; k < len; k++) + { // Repeat the pixel len times. + memcpy (pBuf+(k*4*PixelPerRLEUnit), pBuf, + 4*PixelPerRLEUnit); + } + pBuf += len*4*PixelPerRLEUnit; + } + else + { + expandBuf8 (pBuf, pSrcLine+j+1, 1, pixelSize); + for (k = 1; k < len; k++) + { // Repeat the expanded byte len times. + memcpy (pBuf+(k*PixelPerRLEUnit), pBuf, + PixelPerRLEUnit); + } + pBuf += len*PixelPerRLEUnit; + } + j += 1 + pkpixsize; + } + } + else + { // Unpacked data + len = (FlagCounter & 255) + 1; + if (pixelSize == 16) + { + expandBuf (pBuf, pSrcLine+j+1, len, pixelSize); + pBuf += len*4*PixelPerRLEUnit; + } + else + { + expandBuf8 (pBuf, pSrcLine+j+1, len, pixelSize); + pBuf += len*PixelPerRLEUnit; + } + j += len * pkpixsize + 1; + } + } + pDestLine = pLineArray[i]; + if (pixelSize == 16) + memcpy (pDestLine, pLineBuf, 4*Width); + else + memcpy (pDestLine, pLineBuf, Width); + } + } + } + catch (PLTextException) + { + delete [] pLineBuf; + throw; + } + + delete [] pLineBuf; +} + +void PLPictDecoder::skipBits + ( MacRect* pBounds, + PLWORD rowBytes, + int pixelSize, // Source bits per pixel. + PLDataSource * pDataSrc + ) + // skips unneeded packbits. +{ + int i; + PLWORD pixwidth; // bytes per row when uncompressed. + int linelen; // length of source line in bytes. + + int Height = pBounds->bottom - pBounds->top; + int Width = pBounds->right - pBounds->left; + + // High bit of rowBytes is flag. + if (pixelSize <= 8) + rowBytes &= 0x7fff; + + pixwidth = Width; + + if (pixelSize == 16) + pixwidth *= 2; + + if (rowBytes == 0) + rowBytes = pixwidth; + + if (rowBytes < 8) + { + pDataSrc->Skip (rowBytes*Height); + } + else + { + for (i = 0; i < Height; i++) + { + if (rowBytes > 250) + linelen = ReadMWord(pDataSrc); + else + linelen = ReadByte(pDataSrc); + pDataSrc->Skip (linelen); + } + } +} + + +void PLPictDecoder::expandBuf + ( PLBYTE * pDestBuf, + PLBYTE * pSrcBuf, + int Width, // Width in bytes for 8 bpp or less. + // Width in pixels for 16 bpp. + int bpp // bits per pixel + ) + // Expands Width units to 32-bit pixel data. +{ + PLBYTE * pSrc; + PLBYTE * pDest; + int i; + + pSrc = pSrcBuf; + pDest = pDestBuf; + + switch (bpp) + { + case 16: + for (i=0; i> 5) & 31)*8; // Green + *(pDestBuf+PL_RGBA_RED) = ((Src >> 10) & 31)*8; // Red + *(pDestBuf+PL_RGBA_ALPHA) = 0xFF; // Alpha + pSrcBuf += 2; + pDestBuf += 4; + } + break; + default: + raiseError (PL_ERRFORMAT_UNKNOWN, + "Bad bits per pixel in expandBuf."); + } + return; +} + + +void PLPictDecoder::expandBuf8 + ( PLBYTE * pDestBuf, + PLBYTE * pSrcBuf, + int Width, // Width in bytes. + int bpp // bits per pixel. + ) + // Expands Width units to 8-bit pixel data. + // Max. 8 bpp source format. +{ + PLBYTE * pSrc; + PLBYTE * pDest; + int i; + + pSrc = pSrcBuf; + pDest = pDestBuf; + + switch (bpp) + { + case 8: + memcpy (pDestBuf, pSrcBuf, Width); + break; + case 4: + for (i=0; i> 4) & 15; + *(pDest+1) = (*pSrc & 15); + pSrc++; + pDest += 2; + } + if (Width & 1) // Odd Width? + { + *pDest = (*pSrc >> 4) & 15; + pDest++; + } + break; + case 2: + for (i=0; i> 6) & 3; + *(pDest+1) = (*pSrc >> 4) & 3; + *(pDest+2) = (*pSrc >> 2) & 3; + *(pDest+3) = (*pSrc & 3); + pSrc++; + pDest += 4; + } + if (Width & 3) // Check for leftover pixels + for (i=6; i>8-(Width & 3)*2; i-=2) + { + *pDest = (*pSrc >> i) & 3; + pDest++; + } + break; + case 1: + for (i=0; i> 7) & 1; + *(pDest+1) = (*pSrc >> 6) & 1; + *(pDest+2) = (*pSrc >> 5) & 1; + *(pDest+3) = (*pSrc >> 4) & 1; + *(pDest+4) = (*pSrc >> 3) & 1; + *(pDest+5) = (*pSrc >> 2) & 1; + *(pDest+6) = (*pSrc >> 1) & 1; + *(pDest+7) = (*pSrc & 1); + pSrc++; + pDest += 8; + } + if (Width & 7) // Check for leftover pixels + for (i=7; i>(8-Width & 7); i--) + { + *pDest = (*pSrc >> i) & 1; + pDest++; + } + break; + default: + raiseError (PL_ERRFORMAT_UNKNOWN, + "Bad bits per pixel in expandBuf8."); + } + return; +} + + +///////////////////////////////////////////////////////////////////// +// Auxillary functions + +void PLPictDecoder::readPixmap + ( MacpixMap * pPixMap, + PLDataSource * pDataSrc + ) +{ + pPixMap->version = ReadMWord(pDataSrc); + pPixMap->packType = ReadMWord(pDataSrc); + pPixMap->packSize = ReadMLong(pDataSrc); + pPixMap->hRes = ReadMWord(pDataSrc); + ReadMWord(pDataSrc); + pPixMap->vRes = ReadMWord(pDataSrc); + ReadMWord(pDataSrc); + pPixMap->pixelType = ReadMWord(pDataSrc); + pPixMap->pixelSize = ReadMWord(pDataSrc); + pPixMap->cmpCount = ReadMWord(pDataSrc); + pPixMap->cmpSize = ReadMWord(pDataSrc); + pPixMap->planeBytes = ReadMLong(pDataSrc); + pPixMap->pmTable = ReadMLong(pDataSrc); + pPixMap->pmReserved = ReadMLong(pDataSrc); + + tracePixMapHeader (2, pPixMap); +} + +void PLPictDecoder::readColourTable + ( PLWORD * pNumColors, + PLDataSource * pDataSrc, + PLPixel32 * pPal + ) + // Reads a mac colour table into a bitmap palette. +{ + PLLONG ctSeed; + PLWORD ctFlags; + PLWORD val; + int i; + + Trace (3, "Getting color table info.\n"); + + ctSeed = ReadMLong(pDataSrc); + ctFlags = ReadMWord(pDataSrc); + *pNumColors = ReadMWord(pDataSrc)+1; + + char sz[256]; + sprintf (sz, "Palette Size: %d\n", *pNumColors); + Trace (2, sz); + Trace (3, "Reading Palette.\n"); + + for (i = 0; i < *pNumColors; i++) + { + val = ReadMWord(pDataSrc); + if (ctFlags & 0x8000) + // The indicies in a device colour table are bogus and + // usually == 0, so I assume we allocate up the list of + // colours in order. + val = i; + if (val >= *pNumColors) + { + raiseError (PL_ERRFORMAT_UNKNOWN, + "pixel value greater than colour table size."); + } + // Mac colour tables contain 16-bit values for R, G, and B... + pPal[val].SetR ((PLBYTE) (((PLWORD) (ReadMWord(pDataSrc)) >> 8) & 0xFF)); + pPal[val].SetG ((PLBYTE) (((PLWORD) (ReadMWord(pDataSrc)) >> 8) & 0xFF)); + pPal[val].SetB ((PLBYTE) (((PLWORD) (ReadMWord(pDataSrc)) >> 8) & 0xFF)); + } + +} + +void PLPictDecoder::readRect + ( MacRect * pr, + PLDataSource * pDataSrc + ) +{ + pr->top = ReadMWord(pDataSrc); + pr->left = ReadMWord(pDataSrc); + pr->bottom = ReadMWord(pDataSrc); + pr->right = ReadMWord(pDataSrc); +} + + +void PLPictDecoder::dumpRect + ( char * psz, + MacRect * pr + ) +{ + char sz[256]; + sprintf (sz, "%s (%d,%d) (%d,%d).\n", + psz, pr->left, pr->top, pr->right, pr->bottom); + Trace (2, sz); +} + + +void PLPictDecoder::tracePixMapHeader + ( int Level, + MacpixMap * pPixMap + ) +{ + char sz[256]; + Trace (Level, "PixMap header info:\n"); + dumpRect (" Bounds:", &(pPixMap->Bounds)); + + sprintf (sz, " version: 0x%x\n", pPixMap->version); + Trace (Level, sz); + sprintf (sz, " packType: %d\n", pPixMap->packType); + Trace (Level, sz); + sprintf (sz, " packSize: %ld\n", pPixMap->packSize); + Trace (Level, sz); + sprintf (sz, " hRes: %ld\n", pPixMap->hRes); + Trace (Level, sz); + sprintf (sz, " vRes: %ld\n", pPixMap->vRes); + Trace (Level, sz); + sprintf (sz, " pixelSize: %d\n", pPixMap->pixelSize); + Trace (Level, sz); + sprintf (sz, " cmpCount: %d\n", pPixMap->cmpCount); + Trace (Level, sz); + sprintf (sz, " cmpSize: %d.\n", pPixMap->cmpSize); + Trace (Level, sz); + sprintf (sz, " planeBytes: %ld.\n", pPixMap->planeBytes); + Trace (Level, sz); +} + +/* +/-------------------------------------------------------------------- +| +| $Log: plpictdec.cpp,v $ +| Revision 1.14 2004/10/02 22:23:12 uzadow +| - configure and Makefile cleanups\n- Pixelformat enhancements for several filters\n- Added PLBmpBase:Dump\n- Added PLBmpBase::GetPixelNn()-methods\n- Changed default unix byte order to BGR +| +| Revision 1.13 2004/09/11 12:41:35 uzadow +| removed plstdpch.h +| +| Revision 1.12 2004/09/09 16:52:49 artcom +| refactored PixelFormat +| +| Revision 1.11 2004/06/19 16:49:07 uzadow +| Changed GetImage so it works with PLBmpBase +| +| Revision 1.10 2002/11/04 21:03:12 uzadow +| Fixed error when PL_SUPPORT_JPEG was turned off. +| +| Revision 1.9 2002/08/04 21:20:41 uzadow +| no message +| +| Revision 1.8 2002/08/04 20:08:01 uzadow +| Added PLBmpInfo class, ability to extract metainformation from images without loading the whole image and proper greyscale support. +| +| Revision 1.7 2002/03/31 13:36:42 uzadow +| Updated copyright. +| +| Revision 1.6 2001/10/21 17:12:40 uzadow +| Added PSD decoder beta, removed BPPWanted from all decoders, added PLFilterPixel. +| +| Revision 1.5 2001/10/16 17:12:26 uzadow +| Added support for resolution information (Luca Piergentili) +| +| Revision 1.4 2001/10/06 22:03:26 uzadow +| Added PL prefix to basic data types. +| +| Revision 1.3 2001/10/06 15:32:22 uzadow +| Removed types LPBYTE, DWORD, UCHAR, VOID and INT from the code. +| +| Revision 1.2 2001/09/16 20:57:17 uzadow +| Linux version name prefix changes +| +| Revision 1.1 2001/09/16 19:03:22 uzadow +| Added global name prefix PL, changed most filenames. +| +| Revision 1.12 2001/02/04 14:31:52 uzadow +| Member initialization list cleanup (Erik Hoffmann). +| +| Revision 1.11 2001/01/21 14:28:21 uzadow +| Changed array cleanup from delete to delete[]. +| +| Revision 1.10 2000/12/18 22:42:52 uzadow +| Replaced RGBAPIXEL with PLPixel32. +| +| Revision 1.9 2000/10/24 23:01:42 uzadow +| Fixed bug in ver.1 bitmap decoder +| +| Revision 1.8 2000/08/13 12:11:43 Administrator +| Added experimental DirectDraw-Support +| +| Revision 1.7 2000/05/27 16:28:28 Ulrich von Zadow +| Really fixed bug decoding pixmaps with < 8 bpp. +| +| Revision 1.6 2000/03/15 17:23:20 Ulrich von Zadow +| Fixed bug decoding pixmaps with < 8 bpp. +| +| Revision 1.5 2000/01/16 20:43:14 anonymous +| Removed MFC dependencies +| +| Revision 1.4 1999/11/22 15:00:27 Ulrich von Zadow +| Fixed bug decoding small 24 bpp pict files. +| +| Revision 1.3 1999/10/03 18:50:51 Ulrich von Zadow +| Added automatic logging of changes. +| +| +\-------------------------------------------------------------------- +*/ diff --git a/twelvemonkeys-imageio/pict/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/twelvemonkeys-imageio/pict/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi new file mode 100755 index 00000000..1f4a92e9 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi @@ -0,0 +1 @@ +com.twelvemonkeys.imageio.plugins.pict.PICTImageReaderSpi \ No newline at end of file diff --git a/twelvemonkeys-imageio/pict/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi b/twelvemonkeys-imageio/pict/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi new file mode 100755 index 00000000..4a4007f8 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/main/resources/META-INF/services/javax.imageio.spi.ImageWriterSpi @@ -0,0 +1 @@ +com.twelvemonkeys.imageio.plugins.pict.PICTImageWriterSpi \ No newline at end of file diff --git a/twelvemonkeys-imageio/pict/src/test/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReaderTestCase.java b/twelvemonkeys-imageio/pict/src/test/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReaderTestCase.java new file mode 100755 index 00000000..ec2ad5f7 --- /dev/null +++ b/twelvemonkeys-imageio/pict/src/test/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReaderTestCase.java @@ -0,0 +1,60 @@ +package com.twelvemonkeys.imageio.plugins.pict; + +import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase; + +import javax.imageio.spi.ImageReaderSpi; +import java.awt.*; +import java.util.Arrays; +import java.util.List; + +/** + * ICOImageReaderTestCase + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ICOImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ + */ +public class PICTImageReaderTestCase extends ImageReaderAbstractTestCase { + + static ImageReaderSpi sProvider = new PICTImageReaderSpi(); + + // TODO: Should also test the cliboard format (without 512 byte header) + protected List getTestData() { + return Arrays.asList( + new TestData(getClassLoaderResource("/pict/test.pct"), new Dimension(300, 200)), + new TestData(getClassLoaderResource("/pict/food.pct"), new Dimension(146, 194)), + new TestData(getClassLoaderResource("/pict/carte.pict"), new Dimension(782, 598)), + // Embedded QuickTime image... Should at least include the embedded fallback text + new TestData(getClassLoaderResource("/pict/u2.pict"), new Dimension(160, 159)), + // Obsolete V2 format with weird header + new TestData(getClassLoaderResource("/pict/FLAG_B24.PCT"), new Dimension(124, 124)), + // 1000 DPI with bounding box not matching DPI + new TestData(getClassLoaderResource("/pict/oom.pict"), new Dimension(1713, 1263)) + ); + } + + protected ImageReaderSpi createProvider() { + return sProvider; + } + + @Override + protected PICTImageReader createReader() { + return new PICTImageReader(sProvider); + } + + protected Class getReaderClass() { + return PICTImageReader.class; + } + + protected List getFormatNames() { + return Arrays.asList("pict"); + } + + protected List getSuffixes() { + return Arrays.asList("pct", "pict"); + } + + protected List getMIMETypes() { + return Arrays.asList("image/pict", "image/x-pict"); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/1.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/1.pict new file mode 100755 index 00000000..3b4a8611 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/1.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/16bit.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/16bit.pict new file mode 100755 index 00000000..830d2e34 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/16bit.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/2.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/2.pict new file mode 100755 index 00000000..cccdbac0 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/2.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/32bit.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/32bit.pict new file mode 100755 index 00000000..d17c16fe Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/32bit.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/4.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/4.pict new file mode 100755 index 00000000..80eb774a Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/4.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/5.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/5.pict new file mode 100755 index 00000000..7d62beeb Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/5.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/6.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/6.pict new file mode 100755 index 00000000..3bc0c581 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/6.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/7.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/7.pict new file mode 100755 index 00000000..9d68894c Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/7.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/8.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/8.pict new file mode 100755 index 00000000..3386cc7c Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/8.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/9.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/9.pict new file mode 100755 index 00000000..c34290af Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/9.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/BLK.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/BLK.PCT new file mode 100755 index 00000000..03819136 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/BLK.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/BLU.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/BLU.PCT new file mode 100755 index 00000000..667c62ae Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/BLU.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/EDUC0052.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/EDUC0052.pict new file mode 100755 index 00000000..21038982 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/EDUC0052.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/FLAG_B24.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/FLAG_B24.PCT new file mode 100755 index 00000000..d9bc2d53 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/FLAG_B24.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/GRN.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/GRN.PCT new file mode 100755 index 00000000..3ac4ec7d Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/GRN.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/MARBLES.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/MARBLES.PCT new file mode 100755 index 00000000..47ffe741 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/MARBLES.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/RED.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/RED.PCT new file mode 100755 index 00000000..543d5361 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/RED.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/TRU256.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/TRU256.PCT new file mode 100755 index 00000000..e1c2a9a1 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/TRU256.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/VENUS.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/VENUS.PCT new file mode 100755 index 00000000..a50f5829 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/VENUS.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/WHT.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/WHT.PCT new file mode 100755 index 00000000..0cf13ceb Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/WHT.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/YEL.PCT b/twelvemonkeys-imageio/pict/src/test/resources/pict/YEL.PCT new file mode 100755 index 00000000..2f9d44ec Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/YEL.PCT differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/carte.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/carte.pict new file mode 100755 index 00000000..0cfc22ae Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/carte.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/demo.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/demo.pict new file mode 100755 index 00000000..4446f430 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/demo.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/foo.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/foo.pict new file mode 100755 index 00000000..f5879194 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/foo.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/food.pct b/twelvemonkeys-imageio/pict/src/test/resources/pict/food.pct new file mode 100755 index 00000000..c4a4c673 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/food.pct differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/mire16.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/mire16.pict new file mode 100755 index 00000000..e7a328f6 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/mire16.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/mire32.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/mire32.pict new file mode 100755 index 00000000..2d029ef1 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/mire32.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/oom.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/oom.pict new file mode 100755 index 00000000..eec7427a Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/oom.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_bmp.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_bmp.pict new file mode 100755 index 00000000..08b28056 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_bmp.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_jpeg.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_jpeg.pict new file mode 100755 index 00000000..263cfa73 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_jpeg.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_mire.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_mire.pict new file mode 100755 index 00000000..c0f30b52 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_mire.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_png.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_png.pict new file mode 100755 index 00000000..67e2fe68 Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_png.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_tiff.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_tiff.pict new file mode 100755 index 00000000..8b48695d Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/qt_tiff.pict differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/test.pct b/twelvemonkeys-imageio/pict/src/test/resources/pict/test.pct new file mode 100755 index 00000000..b8f7bc2f Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/test.pct differ diff --git a/twelvemonkeys-imageio/pict/src/test/resources/pict/u2.pict b/twelvemonkeys-imageio/pict/src/test/resources/pict/u2.pict new file mode 100755 index 00000000..f9b6a27c Binary files /dev/null and b/twelvemonkeys-imageio/pict/src/test/resources/pict/u2.pict differ diff --git a/twelvemonkeys-imageio/pom.xml b/twelvemonkeys-imageio/pom.xml new file mode 100644 index 00000000..c2c1e98a --- /dev/null +++ b/twelvemonkeys-imageio/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + com.twelvemonkeys + twelvemonkeys-imageio + 2.2 + pom + TwelveMonkeys ImageIO + + + com.twelvemonkeys + twelvemonkeys-parent + 2.0 + + + + + Harald Kuhr + harald.kuhr@gmail.com + + owner + developer + + + + + + + core + + + ico + iff + pdf + pict + psd + thumbsdb + + + batik + jmagick + + + + 2.2-SNAPSHOT + 2.2-SNAPSHOT + + + + + com.twelvemonkeys + twelvemonkeys-core + ${core.version} + compile + + + com.twelvemonkeys + twelvemonkeys-core + ${core.version} + tests + test + + + + junit + junit + 4.3.1 + test + + + + jmock + jmock-cglib + 1.0.1 + test + + + + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + ${imageio.core.version} + compile + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + ${imageio.core.version} + tests + test + + + + + + + + maven-source-plugin + + + + maven-resources-plugin + + UTF-8 + + + + + + diff --git a/twelvemonkeys-imageio/psd/license.txt b/twelvemonkeys-imageio/psd/license.txt new file mode 100755 index 00000000..2d8ee79c --- /dev/null +++ b/twelvemonkeys-imageio/psd/license.txt @@ -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. \ No newline at end of file diff --git a/twelvemonkeys-imageio/psd/pom.xml b/twelvemonkeys-imageio/psd/pom.xml new file mode 100755 index 00000000..81abcf08 --- /dev/null +++ b/twelvemonkeys-imageio/psd/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + com.twelvemonkeys.imageio + twelvemonkeys-imageio-psd + 2.2-SNAPSHOT + TwelveMonkeys ImageIO PSD plugin + + ImageIO plugin for Adobe Photoshop Document (PSD). + + + + twelvemonkeys-imageio + com.twelvemonkeys + 2.2 + + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + tests + + + \ No newline at end of file diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/CMYKColorSpace.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/CMYKColorSpace.java new file mode 100755 index 00000000..11c01e89 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/CMYKColorSpace.java @@ -0,0 +1,112 @@ +/* + * 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.psd; + +import java.awt.color.ColorSpace; + +/** + * CMYKColorSpace +* +* @author Harald Kuhr +* @author last modified by $Author: haraldk$ +* @version $Id: CMYKColorSpace.java,v 1.0 Apr 30, 2008 1:38:13 PM haraldk Exp$ +*/ +// TODO: Move to com.twlevemonkeys.image? +// TODO: Read a ICC CMYK profile from classpath resource (from ECI)? ISO coated? +final class CMYKColorSpace extends ColorSpace { + + static final ColorSpace INSTANCE = new CMYKColorSpace(); + final ColorSpace sRGB = getInstance(CS_sRGB); + + CMYKColorSpace() { + super(ColorSpace.TYPE_CMYK, 4); + } + + public static ColorSpace getInstance() { + return INSTANCE; + } + + public float[] toRGB(float[] colorvalue) { + return new float[] { + (1 - colorvalue[0]) * (1 - colorvalue[3]), + (1 - colorvalue[1]) * (1 - colorvalue[3]), + (1 - colorvalue[2]) * (1 - colorvalue[3]) + }; + // TODO: Convert via CIEXYZ space using sRGB space, as suggested in docs + // return sRGB.fromCIEXYZ(toCIEXYZ(colorvalue)); + } + + public float[] fromRGB(float[] rgbvalue) { + // Compute CMY + float c = 1 - rgbvalue[0]; + float m = 1 - rgbvalue[1]; + float y = 1 - rgbvalue[2]; + + // Find K + float k = Math.min(c, Math.min(m, y)); + + // Convert to CMYK values + return new float[] {(c - k), (m - k), (y - k), k}; +/* +http://www.velocityreviews.com/forums/t127265-rgb-to-cmyk.html + +(Step 0: Normalize R,G, and B values to fit into range [0.0 ... 1.0], or +adapt the following matrix.) + +Step 1: RGB to CMY + +| C | | 1 | | R | +| M | = | 1 | - | G | +| Y | | 1 | | B | + +Step 2: CMY to CMYK + +| C' | | C | | min(C,M,Y) | +| M' | | M | | min(C,M,Y) | +| Y' | = | Y | - | min(C,M,Y) | +| K' | | min(C,M,Y) | | 0 | + +Easier to calculate if K' is calculated first, because K' = min(C,M,Y): + +| C' | | C | | K' | +| M' | | M | | K' | +| Y' | = | Y | - | K' | +| K' | | K'| | 0 | + */ +// return fromCIEXYZ(sRGB.toCIEXYZ(rgbvalue)); + } + + public float[] toCIEXYZ(float[] colorvalue) { + throw new UnsupportedOperationException("Method toCIEXYZ not implemented"); // TODO: Implement + } + + public float[] fromCIEXYZ(float[] colorvalue) { + throw new UnsupportedOperationException("Method fromCIEXYZ not implemented"); // TODO: Implement + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/ICCProfile.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/ICCProfile.java new file mode 100755 index 00000000..88474984 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/ICCProfile.java @@ -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.psd; + +import com.twelvemonkeys.imageio.util.IIOUtil; + +import javax.imageio.stream.ImageInputStream; +import java.awt.color.ICC_Profile; +import java.io.IOException; +import java.io.InputStream; + +/** + * ICCProfile + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ICCProfile.java,v 1.0 May 20, 2008 6:24:10 PM haraldk Exp$ + */ +class ICCProfile extends PSDImageResource { + private ICC_Profile mProfile; + + ICCProfile(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(ImageInputStream pInput) throws IOException { + InputStream stream = IIOUtil.createStreamAdapter(pInput, mSize); + try { + mProfile = ICC_Profile.getInstance(stream); + } + finally { + // Make sure stream has correct position after read + stream.close(); + } + } + + public ICC_Profile getProfile() { + return mProfile; + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", profile: ").append(mProfile); + builder.append("]"); + + return builder.toString(); + } + +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java new file mode 100755 index 00000000..30c77218 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java @@ -0,0 +1,544 @@ +/* + * 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.psd; + +/** + * PSD format constants. + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSD.java,v 1.0 Apr 29, 2008 4:47:47 PM haraldk Exp$ + * + * @see http://www.fileformat.info/format/psd/egff.htm + */ +interface PSD { + /** PSD 2+ Native format (.PSD) identifier "8BPS" */ + int SIGNATURE_8BPS = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'S'; + + /** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */ + int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B'; + + /** PSD Resource type identifier "8BIM" */ + int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M'; + + // Blending modes + /** Normal blending mode "norm"*/ + int BLEND_NORM = ('n' << 24) + ('o' << 16) + ('r' << 8) + 'm'; + + /** Darken blending mode "dark" */ + int BLEND_DARK = ('d' << 24) + ('a' << 16) + ('r' << 8) + 'k'; + + /** Lighten blending mode "lite" */ + int BLEND_LITE = ('l' << 24) + ('i' << 16) + ('t' << 8) + 'e'; + + /** Hue blending mode "hue " */ + int BLEND_HUE = ('h' << 24) + ('u' << 16) + ('e' << 8) + ' '; + + /** Saturation blending mode "sat " */ + int BLEND_SAT = ('s' << 24) + ('a' << 16) + ('t' << 8) + ' '; + + /** Color blending mode "colr" */ + int BLEND_COLR = ('c' << 24) + ('o' << 16) + ('l' << 8) + 'r'; + + /** Luminosity blending mode "lum " */ + int BLEND_LUM = ('l' << 24) + ('u' << 16) + ('m' << 8) + ' '; + + /** Multiply blending mode "mul " */ + int BELND_MUL = ('m' << 24) + ('u' << 16) + ('l' << 8) + ' '; + + /** Screen blending mode "scrn" */ + int BLEND_SCRN = ('s' << 24) + ('c' << 16) + ('r' << 8) + 'n'; + + /** Dissolve blending mode "diss" */ + int BLEND_DISS = ('d' << 24) + ('i' << 16) + ('s' << 8) + 's'; + + /** Overlay blending mode "over" */ + int BLEND_OVER = ('o' << 24) + ('v' << 16) + ('e' << 8) + 'r'; + + /** Hard light blending mode "hLit" */ + int BLEND_HLIT = ('h' << 24) + ('L' << 16) + ('i' << 8) + 't'; + + /** Soft light blending mode "sLit" */ + int BLEND_SLIT = ('s' << 24) + ('L' << 16) + ('i' << 8) + 't'; + + /** Difference blending mode "diff" */ + int BLEND_DIFF = ('d' << 24) + ('i' << 16) + ('f' << 8) + 'f'; + + // Compression modes + /** No compression */ + int COMPRESSION_NONE = 0; + + /** PacBits RLE compression */ + int COMPRESSION_RLE = 1; + + /** ZIP compression */ + int COMPRESSION_ZIP = 2; + + /** ZIP compression with prediction */ + int COMPRESSION_ZIP_PREDICTON = 3; + + // Color Modes + /** Bitmap (monochrome) */ + short COLOR_MODE_MONOCHROME = 0; + + /** Gray-scale */ + short COLOR_MODE_GRAYSCALE = 1; + + /** Indexed color (palette color) */ + short COLOR_MODE_INDEXED = 2; + + /** RGB color */ + short COLOR_MODE_RGB = 3; + + /** CMYK color */ + short COLOR_MODE_CMYK = 4; + + /** Multichannel color */ + short COLOR_MODE_MULTICHANNEL = 7; + + /** Duotone (halftone) */ + short COLOR_MODE_DUOTONE = 8; + + /** Lab color */ + short COLOR_MODE_LAB = 9; + + // TODO: Consider moving these constants to PSDImageResource + // ID values 03e8, 03eb, 03ff, and 0403 are considered obsolete. Values 03e8 and 03eb are associated with + // Photoshop v2.0. The data format for values 03f2, 03f4-03fa, 03fc, 03fd, 0405-0bb7 is intentionally not + // documented by Adobe, or the data is missing. + // Please refer to the Adobe Photoshop SDK for information on obtaining the IPTC-NAA record 2 structure definition. + // WORD[5] + /** Channels, rows, columns, depth, and mode (Obsolete?Photoshop 2.0 only). */ + int RES_CHANNELS_ROWS_COLUMNS_DEPTH_MODE = 0x03e8; + + /** Optional Macintosh print manager information. */ + int RES_MAC_PRINT_MANAGER_INFO = 0x03e9; + + /** Indexed color table (Obsolete?Photoshop 2.0 only). */ + int RES_INDEXED_COLOR_TABLE = 0x03eb; + + /** Resolution information + // ID value 03ed indicates that the data is in the form of a ResolutionInfo structure: + // + // typedef struct _ResolutionInfo + // { + // LONG hRes; // Fixed-point number: pixels per inch + // WORD hResUnit; // 1=pixels per inch, 2=pixels per centimeter + // WORD WidthUnit; // 1=in, 2=cm, 3=pt, 4=picas, 5=columns + // LONG vRes; // Fixed-point number: pixels per inch/ + // WORD vResUnit; // 1=pixels per inch, 2=pixels per centimeter + // WORD HeightUnit; // 1=in, 2=cm, 3=pt, 4=picas, 5=columns + // } RESOLUTIONINFO; + */ + int RES_RESOLUTION_INFO = 0x3ed; + + /** Alpha channel names (Pascal-format strings) */ + int RES_ALPHA_CHANNEL_INFO = 0x3ee; + + /** Display information for each channel + // ID value 03ef indicates that the data is stored as a DisplayInfo structure, which contains display information + // associated with each channel: + // + // typedef _DisplayInfo + // { + // WORD ColorSpace; + // WORD Color[4]; + // WORD Opacity; // 0-100 + // BYTE Kind; // 0=selected, 1=protected + // BYTE Padding; // Always zero + // } DISPLAYINFO; + // + */ + int RES_DISPLAY_INFO = 0x3ef; + + // 03f0 + // BYTE[] + /** Optional Pascal-format caption string */ + int RES_CAPTION = 0x03f0; + + // 03f1 + // LONG, WORD + /** Fixed-point border width, border units */ + int RES_BORDER_WIDTH = 0x03f1; + + // 03f2 + /** Background color */ + // 2 byte Color space: 0 = RGB (unsigned 16 bit), 1 = HSB (unsigned 16 bit), 2 = CMYK (unsigned 16 bit), + // 3 = Pantone matching system (undocumented), 4 = Focoltone colour system (undocumented), + // 5 = Truematch color (undocumented), 6 = Toyo 88 colorfinder 1050 (undocumented), + // 7 = Lab (lighntess 0...10000, chrominance -12800..127000, 8 = Grayscale 0...10000, + // 10 = HKS colors + // 8 byte Color data: 6 first bytes used for RGB, HSB and Lab, all 8 for CMYK and only first two for grayscale + int RES_BACKGROUND_COLOR = 0x03f2; + + // 03f3 + // BYTE[8] + /** + * Print flags. + * ID value 03f3 indicates that the data is a series of eight flags, indicating the enabled state of labels, + * crop marks, color bars, registration marks, negative, flip, interpolate, and caption items in the + * Photoshop Page Setup dialog box. + */ + int RES_PRINT_FLAGS = 0x03f3; + + // 03f4 + /** Gray-scale and halftoning information */ + int RES_GRAYSCALE_HALFTONE_INFO = 0x03f4; + + // 03f5 + /** Color halftoning information */ + int RES_COLOR_HALFTONE_INFO = 0x03f5; + + // 03f6 + /** Duotone halftoning information */ + int RES_DUOTONE_HALFTONE_INFO = 0x03f6; + + // 03f7 + /** Gray-scale and multichannel transfer function */ + int RES_GRAYSCALE_MULTICHANNEL_TRANSFER_FUNCTION = 0x03f7; + + // 03f8 + /** Color transfer functions */ + int RES_COLOR_TRANSFER_FUNCITON = 0x03f8; + + // 03f9 + /** Duotone transfer functions */ + int RES_DUOTONE_TRANSFER_FUNCITON = 0x03f9; + + // 03fa + /** Duotone image information */ + int RES_DUOTONE_IMAGE_INFO = 0x03fa; + + // 03fb + // BYTE[2] + /** Effective black and white value for dot range */ + int RES_EFFECTIVE_BLACK_WHITE = 0x03fb; + + // 03fc + /** Obsolete undocumented resource. */ + int RES_03FC = 0x03fc; + + // 03fd + /** EPS options */ + int RES_EPS_OPTIONS = 0x03fd; + + // 03fe + // WORD, BYTE + /** Quick Mask channel ID, flag for mask initially empty */ + int RES_QUICK_MASK_CHANNEL_ID = 0x03fe; + + // 03ff + /** Obsolete undocumented resource. */ + int RES_03ff = 0x03ff; + + // 0400 + // WORD + /** Index of target layer (0=bottom)*/ + int RES_INDEX_OF_TARGET_LAYER = 0x0400; + + // 0401 + /** Working path */ + int RES_WORKING_PATH = 0x0401; + + // 0402 + // WORD[] + /** Layers group info, group ID for dragging groups */ + int RES_LAYERS_GROUP_INFO = 0x0402; + + // + // 0403 + /** Obsolete undocumented resource. */ + int RES_0403 = 0x0403; + + // 0404 + /** IPTC-NAA record */ + int RES_IPTC_NAA = 0x0404; + + // 0405 + /** Image mode for raw-format files */ + int RES_RAW_IMAGE_MODE = 0x0405; + + // 0406 + /** JPEG quality (Adobe internal) */ + int RES_JPEG_QUALITY = 0x0406; + + // 1032 + /** (Photoshop 4.0) Grid and guides information */ + int RES_GRID_AND_GUIDES_INFO = 0x0408; + + // 1033 + /** + * (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only. BGR layout. Obsolete. + * @see #RES_THUMBNAIL + */ + int RES_THUMBNAIL_PS4 = 0x0409; + + // 1034 + /** + * (Photoshop 4.0) Copyright flag + * Boolean indicating whether image is copyrighted. Can be set via + * Property suite or by user in File Info... + */ + int RES_COPYRIGHT_FLAG = 0x040A; + + // 1035 + /** + * (Photoshop 4.0) URL + * Handle of a text string with uniform resource locator. Can be set via + * Property suite or by user in File Info... + */ + int RES_URL = 0x040B; + + // 1036 + /** (Photoshop 5.0) Thumbnail resource (supersedes resource 1033) */ + int RES_THUMBNAIL = 0x040C; + + // 1037 + /** + * (Photoshop 5.0) Global Angle + * 4 bytes that contain an integer between 0 and 359, which is the global + * lighting angle for effects layer. If not present, assumed to be 30. + */ + int RES_GLOBAL_ANGLE = 0x040D; + + // 1038 + /** + * (Photoshop 5.0) Color samplers resource + * See "Color samplers resource format" on page20. + */ + int RES_COLOR_SAMPLERS = 0x040E; + + /** + * (Photoshop 5.0) ICC Profile + * The raw bytes of an ICC (International Color Consortium) format profile. + */ + int RES_ICC_PROFILE = 0x040f; + + // 1040 + /** + * (Photoshop 5.0) Watermark + * One byte. + */ + int RES_WATERMARK = 0x0410; + + // 1041 + /** + * (Photoshop 5.0) ICC Untagged Profile + * 1 byte that disables any assumed profile handling when opening the file. + * 1 = intentionally untagged. + */ + int RES_ICC_UNTAGGED_PROFILE = 0x0411; + + // 1042 + /** + * (Photoshop 5.0) Effects visible + * 1-byte global flag to show/hide all the effects layer. Only present when + * they are hidden. + */ + int RES_EFFECTS_VISIBLE = 0x0412; + + // 1043 + /** + * (Photoshop 5.0) Spot Halftone + * 4 bytes for version, 4 bytes for length, and the variable length data. + */ + int RES_SPOT_HALFTONE = 0x0413; + + // 1044 + /** + * (Photoshop 5.0) Document-specific IDs seed number + * 4 bytes: Base value, starting at which layer IDs will be generated (or a + * greater value if existing IDs already exceed it). Its purpose is to avoid the + * case where we add layers, flatten, save, open, and then add more layers + * that end up with the same IDs as the first set. + */ + int RES_DOC_ID_SEED = 0x0414; + + // 1045 + /** + * (Photoshop 5.0) Unicode Alpha Names + * Unicode string (4 bytes length followed by string). + */ + int RES_UNICODE_ALPHA_NAME = 0x0415; + + // 1046 + /** + * (Photoshop 6.0) Indexed Color Table Count + * 2 bytes for the number of colors in table that are actually defined + */ + int RES_INDEXED_COLOR_TABLE_COUNT = 0x0416; + + //1047 + /** + * (Photoshop 6.0) Transparency Index. + * 2 bytes for the index of transparent color, if any. + */ + int RES_TRANSPARENCY_INDEX = 0x0417; + + //1049 + /** + * (Photoshop 6.0) Global Altitude + * 4 byte entry for altitude + */ + int RES_GLOBAL_ALTITUDE = 0x0419; + + //1050 + /** + * (Photoshop 6.0) Slices + */ + int RES_SLICES = 0x041A; + + //1051 + /** + * (Photoshop 6.0) Workflow URL + * Unicode string + */ + int RES_WORKFLOW_URL = 0x041B; + + // 1052 + /** + * (Photoshop 6.0) Jump To XPEP + * 2 bytes major version, 2 bytes minor version, 4 bytes count. Following is + * repeated for count: 4 bytes block size, 4 bytes key, if key = 'jtDd', then + * next is a Boolean for the dirty flag; otherwise its a 4 byte entry for the + * mod date. + */ + int RES_JUMP_TO_XPEP = 0x041C; + + // 1053 + /** + * (Photoshop 6.0) Alpha Identifiers + * 4 bytes of length, followed by 4 bytes each for every alpha identifier. + */ + int RES_ALPHA_IDENTIFIERS = 0x041D; + + // 1054 + /** + * (Photoshop 6.0) URL List + * 4 byte count of URLs, followed by 4 byte long, 4 byte ID, and Unicode + * string for each count. + */ + int RES_URL_LIST = 0x041E; + + // 1057 + /** + * (Photoshop 6.0) Version Info + * 4 bytes version, 1 byte hasRealMergedData, Unicode string: writer + * name, Unicode string: reader name, 4 bytes file version. + */ + int RES_VERSION_INFO = 0x0421; + + // 1058 + /** + * (Photoshop 7.0) EXIF data 1 + * + * @see EXIF standard + */ + int RES_EXIF_DATA_1 = 0x0422; + + //1059 + /** + * (Photoshop 7.0) EXIF data 3 + * + * @see EXIF standard + */ + int RES_EXIF_DATA_3 = 0x0423; + + //1060 + /** + * (Photoshop 7.0) XMP metadata + * File info as XML description. + * + * @see XMP standard + */ + int RES_XMP_DATA = 0x0424; + + // 1061 + /** + * (Photoshop 7.0) Caption digest + * 16 bytes: RSA Data Security, MD5 message-digest algorithm + */ + int RES_CAPTION_DIGEST = 0x0425; + + // 1062 + /** + * (Photoshop 7.0) Print scale + * 2 bytes style (0 = centered, 1 = size to fit, 2 = user defined). 4 bytes x + * location (floating point). 4 bytes y location (floating point). 4 bytes scale + * (floating point) + */ + int RES_PRINT_SCALE = 0x0426; + + // 1064 + /** + * (Photoshop CS) Pixel Aspect Ratio + * 4 bytes (version = 1), 8 bytes double, x / y of a pixel + * 0x0429 1065 (Photoshop CS) Layer Comps + * 4 bytes (descriptor version = 16), Descriptor (see ?Descriptor structure? + * on page57) + */ + int RES_PIXEL_ASPECT_RATIO = 0x0428; + + // 1066 + /** + * (Photoshop CS) Alternate Duotone Colors + * 2 bytes (version = 1), 2 bytes count, following is repeated for each count: + * [ Color: 2 bytes for space followed by 4 * 2 byte color component ], + * following this is another 2 byte count, usually 256, followed by Lab colors + * one byte each for L, a, b + * This resource is not read or used by Photoshop. + */ + int RES_ALTERNATE_DUOTONE_COLORS = 0x042A; + + // 1067 + /** + * (Photoshop CS) Alternate Spot Colors + * 2 bytes (version = 1), 2 bytes channel count, following is repeated for + * each count: 4 bytes channel ID, Color: 2 bytes for space followed by 4 * 2 + * byte color component + * This resource is not read or used by Photoshop. + */ + int RES_ALTERNATE_SPOT_COLORS = 0x042B; + + // 07d0-0bb6 + /* Saved path information */ + + // 0bb7 + /** Clipping path name */ + int RES_CLIPPING_PATH_NAME = 0x0bb7; + + // 2710 + /** Print flags information + * ID value 2710 signals that the Data section contains a WORD-length version number (should be 1), + * a BYTE-length flag indicating crop marks, a BYTE-length field (should be 0), a LONG-length bleed width value, and a + * WORD indicating the bleed width scale. + */ + int RES_PRINT_FLAGS_INFORMATION = 0x2710; + +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java new file mode 100755 index 00000000..7d3ae079 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java @@ -0,0 +1,67 @@ +/* + * 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.psd; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * PSDAlhpaChannelInfo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDAlhpaChannelInfo.java,v 1.0 May 2, 2008 5:33:40 PM haraldk Exp$ + */ +class PSDAlphaChannelInfo extends PSDImageResource { + List mNames; + + public PSDAlphaChannelInfo(short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(ImageInputStream pInput) throws IOException { + mNames = new ArrayList(); + long left = mSize; + while (left > 0) { + String name = PSDUtil.readPascalStringByte(pInput); + mNames.add(name); + left -= name.length() + 1; + } + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + builder.append(", alpha channels: ").append(mNames).append("]"); + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelInfo.java new file mode 100755 index 00000000..0267dffc --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelInfo.java @@ -0,0 +1,61 @@ +/* + * 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.psd; + +/** + * PSDChannelInfo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDChannelInfo.java,v 1.0 May 6, 2008 2:46:23 PM haraldk Exp$ + */ +class PSDChannelInfo { + private short mChannelId; + long mLength; + + // typedef struct _CLI + // { + // WORD ChannelID; /* Channel Length Info field one */ + // LONG LengthOfChannelData; /* Channel Length Info field two */ + // } CLI; + public PSDChannelInfo(short pChannelId, long pLength) { + mChannelId = pChannelId; + mLength = pLength; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()); + builder.append("["); + builder.append("channelId: ").append(mChannelId); + builder.append(", length: ").append(mLength); + builder.append("]"); + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelSourceDestinationRange.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelSourceDestinationRange.java new file mode 100755 index 00000000..65735285 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelSourceDestinationRange.java @@ -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.psd; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * PSDChannelSourceDestinationRange + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDChannelSourceDestinationRange.java,v 1.0 May 6, 2008 5:14:13 PM haraldk Exp$ + */ +class PSDChannelSourceDestinationRange { + private String mChannel; + private short mSourceBlack; + private short mSourceWhite; + private short mDestBlack; + private short mDestWhite; + + public PSDChannelSourceDestinationRange(ImageInputStream pInput, String pChannel) throws IOException { + mChannel = pChannel; + mSourceBlack = pInput.readShort(); + mSourceWhite = pInput.readShort(); + mDestBlack = pInput.readShort(); + mDestWhite = pInput.readShort(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()); + + builder.append("[(").append(mChannel); + builder.append("): sourceBlack: ").append(Integer.toHexString(mSourceBlack & 0xffff)); + builder.append(", sourceWhite: ").append(Integer.toHexString(mSourceWhite & 0xffff)); + builder.append(", destBlack: ").append(Integer.toHexString(mDestBlack & 0xffff)); + builder.append(", destWhite: ").append(Integer.toHexString(mDestWhite & 0xffff)); + builder.append("]"); + + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDColorData.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDColorData.java new file mode 100755 index 00000000..ecf1b00a --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDColorData.java @@ -0,0 +1,86 @@ +/* + * 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.psd; + +import com.twelvemonkeys.image.InverseColorMapIndexColorModel; + +import javax.imageio.IIOException; +import javax.imageio.stream.ImageInputStream; +import java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; +import java.io.IOException; + +/** + * PSDColorData + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDColorData.java,v 1.0 Apr 29, 2008 5:33:01 PM haraldk Exp$ + */ +class PSDColorData { + final byte[] mColors; + private IndexColorModel mColorModel; + + PSDColorData(ImageInputStream pInput) throws IOException { + int length = pInput.readInt(); + if (length == 0) { + throw new IIOException("No palette information in PSD"); + } + else if (length % 3 != 0) { + throw new IIOException("Wrong palette information in PSD"); + } + + // NOTE: Spec says length may only be 768 bytes (256 RGB triplets) + mColors = new byte[length]; + pInput.readFully(mColors); + + // NOTE: Could be a padding byte here, if not even.. + } + + IndexColorModel getIndexColorModel() { + if (mColorModel == null) { + int[] rgb = toInterleavedRGB(mColors); + mColorModel = new InverseColorMapIndexColorModel(8, rgb.length, rgb, 0, false, -1, DataBuffer.TYPE_BYTE); + } + return mColorModel; + } + + private int[] toInterleavedRGB(byte[] pColors) { + int[] rgb = new int[pColors.length / 3]; + for (int i = 0; i < rgb.length; i++) { + // Pack the non-interleaved samples into interleaved form + int r = pColors[ i] & 0xff; + int g = pColors[ rgb.length + i] & 0xff; + int b = pColors[2 * rgb.length + i] & 0xff; + + rgb[i] = (r << 16) | (g << 8) | b; + } + return rgb; + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDisplayInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDisplayInfo.java new file mode 100755 index 00000000..6b04cf12 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDDisplayInfo.java @@ -0,0 +1,120 @@ +/* + * 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.psd; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.IIOException; +import java.io.IOException; + +/** + * PSDResolutionInfo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$ + */ +class PSDDisplayInfo extends PSDImageResource { + // TODO: Size of this struct should be 14.. Does not compute... + //typedef _DisplayInfo + //{ + // WORD ColorSpace; + // WORD Color[4]; + // WORD Opacity; /* 0-100 */ + // BYTE Kind; /* 0=selected, 1=protected */ + // BYTE Padding; /* Always zero */ + //} DISPLAYINFO; + + private int mColorSpace; + private short[] mColors; + private short mOpacity; + private byte mKind; + + PSDDisplayInfo(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(ImageInputStream pInput) throws IOException { + if (mSize % 14 != 0) { + throw new IIOException("Display info length expected to be mod 14: " + mSize); + } + +// long left = mSize; +// while (left > 0) { + mColorSpace = pInput.readShort(); + + // Color[4]...? + mColors = new short[4]; + mColors[0] = pInput.readShort(); + mColors[1] = pInput.readShort(); + mColors[2] = pInput.readShort(); + mColors[3] = pInput.readShort(); + + mOpacity = pInput.readShort(); + + mKind = pInput.readByte(); + + pInput.readByte(); // Pad +// left -= 14; +// } + pInput.skipBytes(mSize - 14); + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", ColorSpace: ").append(mColorSpace); + builder.append(", Colors: {"); + builder.append(mColors[0]); + builder.append(", "); + builder.append(mColors[1]); + builder.append(", "); + builder.append(mColors[2]); + builder.append(", "); + builder.append(mColors[3]); + builder.append("}, Opacity: ").append(mOpacity); + builder.append(", Kind: ").append(kind(mKind)); + + builder.append("]"); + + return builder.toString(); + } + + private String kind(final byte pKind) { + switch (pKind) { + case 0: + return "selected"; + case 1: + return "protected"; + default: + return "unknown kind: " + Integer.toHexString(pKind & 0xff); + } + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java new file mode 100755 index 00000000..84ebb4e4 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDEXIF1Data.java @@ -0,0 +1,327 @@ +package com.twelvemonkeys.imageio.plugins.psd; + +import com.twelvemonkeys.imageio.util.IIOUtil; +import com.twelvemonkeys.lang.StringUtil; + +import javax.imageio.IIOException; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.MemoryCacheImageInputStream; +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * EXIF metadata. + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: XMPData.java,v 1.0 Jul 28, 2009 5:50:34 PM haraldk Exp$ + * + * @see Wikipedia + * @see Aware systems TIFF tag reference + * @see Adobe TIFF developer resources + */ +public final class PSDEXIF1Data extends PSDImageResource { +// protected byte[] mData; + protected Directory mDirectory; + + PSDEXIF1Data(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(final ImageInputStream pInput) throws IOException { + // This is in essence an embedded TIFF file. + // TODO: Extract TIFF parsing to more general purpose package + // TODO: Instead, read the byte data, store for later parsing + MemoryCacheImageInputStream stream = new MemoryCacheImageInputStream(IIOUtil.createStreamAdapter(pInput, mSize)); + + byte[] bom = new byte[2]; + stream.readFully(bom); + if (bom[0] == 'I' && bom[1] == 'I') { + stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); + } + else if (!(bom[0] == 'M' && bom[1] == 'M')) { + throw new IIOException(String.format("Invalid byte order marker '%s'", StringUtil.decode(bom, 0, bom.length, "ASCII"))); + } + + if (stream.readUnsignedShort() != 42) { + throw new IIOException("Wrong TIFF magic in EXIF data."); + } + + long directoryOffset = stream.readUnsignedInt(); + mDirectory = Directory.read(stream, directoryOffset); + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", ").append(mDirectory); + + builder.append("]"); + + return builder.toString(); + } + + // TIFF Image file directory (IFD) + private static class Directory { + List mEntries = new ArrayList(); + + private Directory() {} + + public static Directory read(final ImageInputStream pInput, final long pOffset) throws IOException { + Directory directory = new Directory(); + + pInput.seek(pOffset); + int entryCount = pInput.readUnsignedShort(); + for (int i = 0; i < entryCount; i++) { + directory.mEntries.add(Entry.read(pInput)); + } + + long nextOffset = pInput.readUnsignedInt(); + if (nextOffset != 0) { + Directory next = Directory.read(pInput, nextOffset); + directory.mEntries.addAll(next.mEntries); + } + + return directory; + } + + @Override + public String toString() { + return String.format("Directory%s", mEntries); + } + } + + // TIFF IFD Entry + private static class Entry { + private static final int EXIF_IFD = 0x8769; + + private final static String[] TYPE_NAMES = { + "BYTE", "ASCII", "SHORT", "LONG", "RATIONAL", + + "SBYTE", "UNDEFINED", "SSHORT", "SLONG", "SRATIONAL", "FLOAT", "DOUBLE", + }; + + private final static int[] TYPE_LENGTHS = { + 1, 1, 2, 4, 8, + + 1, 1, 2, 4, 8, 4, 8, + }; + + private int mTag; + /* + 1 = BYTE 8-bit unsigned integer. + 2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte + must be NUL (binary zero). + 3 = SHORT 16-bit (2-byte) unsigned integer. + 4 = LONG 32-bit (4-byte) unsigned integer. + 5 = RATIONAL Two LONGs: the first represents the numerator of a + fraction; the second, the denominator. + + TIFF 6.0 and above: + 6 = SBYTE An 8-bit signed (twos-complement) integer. + 7 = UNDEFINED An 8-bit byte that may contain anything, depending on + the definition of the field. + 8 = SSHORT A 16-bit (2-byte) signed (twos-complement) integer. + 9 = SLONG A 32-bit (4-byte) signed (twos-complement) integer. + 10 = SRATIONAL Two SLONGs: the first represents the numerator of a + fraction, the second the denominator. + 11 = FLOAT Single precision (4-byte) IEEE format. + 12 = DOUBLE Double precision (8-byte) IEEE format. + */ + private short mType; + private int mCount; + private long mValueOffset; + private Object mValue; + + private Entry() {} + + public static Entry read(final ImageInputStream pInput) throws IOException { + Entry entry = new Entry(); + + entry.mTag = pInput.readUnsignedShort(); + entry.mType = pInput.readShort(); + entry.mCount = pInput.readInt(); // Number of values + + // TODO: Handle other sub-IFDs + if (entry.mTag == EXIF_IFD) { + long offset = pInput.readUnsignedInt(); + pInput.mark(); + try { + entry.mValue = Directory.read(pInput, offset); + } + finally { + pInput.reset(); + } + } + else { + int valueLength = entry.getValueLength(); + if (valueLength > 0 && valueLength <= 4) { + entry.readValueInLine(pInput); + pInput.skipBytes(4 - valueLength); + } + else { + entry.mValueOffset = pInput.readUnsignedInt(); // This is the *value* iff the value size is <= 4 bytes + entry.readValue(pInput); + } + } + + return entry; + } + + private void readValue(final ImageInputStream pInput) throws IOException { + long pos = pInput.getStreamPosition(); + try { + pInput.seek(mValueOffset); + readValueInLine(pInput); + } + finally { + pInput.seek(pos); + } + } + + private void readValueInLine(ImageInputStream pInput) throws IOException { + mValue = readValueDirect(pInput, mType, mCount); + } + + private static Object readValueDirect(final ImageInputStream pInput, final short pType, final int pCount) throws IOException { + switch (pType) { + case 2: + // TODO: This might be UTF-8 or ISO-8859-1, even though against the spec + byte[] ascii = new byte[pCount]; + pInput.readFully(ascii); + return StringUtil.decode(ascii, 0, ascii.length, "ASCII"); + case 1: + if (pCount == 1) { + return pInput.readUnsignedByte(); + } + case 6: + if (pCount == 1) { + return pInput.readByte(); + } + case 7: + byte[] bytes = new byte[pCount]; + pInput.readFully(bytes); + return bytes; + case 3: + if (pCount == 1) { + return pInput.readUnsignedShort(); + } + case 8: + if (pCount == 1) { + return pInput.readShort(); + } + + short[] shorts = new short[pCount]; + pInput.readFully(shorts, 0, shorts.length); + return shorts; + case 4: + if (pCount == 1) { + return pInput.readUnsignedInt(); + } + case 9: + if (pCount == 1) { + return pInput.readInt(); + } + + int[] ints = new int[pCount]; + pInput.readFully(ints, 0, ints.length); + return ints; + case 11: + if (pCount == 1) { + return pInput.readFloat(); + } + + float[] floats = new float[pCount]; + pInput.readFully(floats, 0, floats.length); + return floats; + case 12: + if (pCount == 1) { + return pInput.readDouble(); + } + + double[] doubles = new double[pCount]; + pInput.readFully(doubles, 0, doubles.length); + return doubles; + + // TODO: Consider using a Rational class + case 5: + if (pCount == 1) { + return pInput.readUnsignedInt() / (double) pInput.readUnsignedInt(); + } + + double[] rationals = new double[pCount]; + for (int i = 0; i < rationals.length; i++) { + rationals[i] = pInput.readUnsignedInt() / (double) pInput.readUnsignedInt(); + } + + return rationals; + case 10: + if (pCount == 1) { + return pInput.readInt() / (double) pInput.readInt(); + } + + double[] srationals = new double[pCount]; + for (int i = 0; i < srationals.length; i++) { + srationals[i] = pInput.readInt() / (double) pInput.readInt(); + } + + return srationals; + + default: + throw new IIOException(String.format("Unknown EXIF type '%s'", pType)); + } + } + + private int getValueLength() { + if (mType > 0 && mType <= TYPE_LENGTHS.length) { + return TYPE_LENGTHS[mType - 1] * mCount; + } + return -1; + } + + private String getTypeName() { + if (mType > 0 && mType <= TYPE_NAMES.length) { + return TYPE_NAMES[mType - 1]; + } + return "Unknown type"; + } + + // TODO: Tag names! + @Override + public String toString() { + return String.format("0x%04x: %s (%s, %d)", mTag, getValueAsString(), getTypeName(), mCount); + } + + public String getValueAsString() { + if (mValue instanceof String) { + return String.format("\"%s\"", mValue); + } + + if (mValue != null && mValue.getClass().isArray()) { + Class type = mValue.getClass().getComponentType(); + if (byte.class == type) { + return Arrays.toString((byte[]) mValue); + } + if (short.class == type) { + return Arrays.toString((short[]) mValue); + } + if (int.class == type) { + return Arrays.toString((int[]) mValue); + } + if (float.class == type) { + return Arrays.toString((float[]) mValue); + } + if (double.class == type) { + return Arrays.toString((double[]) mValue); + } + } + + return String.valueOf(mValue); + } + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java new file mode 100755 index 00000000..3e919563 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java @@ -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.psd; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * PSDGlobalLayerMask + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$ + */ +class PSDGlobalLayerMask { + private int mColorSpace; + private int mColor1; + private int mColor2; + private int mColor3; + private int mColor4; + private int mOpacity; + private int mKind; + + PSDGlobalLayerMask(ImageInputStream pInput) throws IOException { + mColorSpace = pInput.readUnsignedShort(); + + mColor1 = pInput.readUnsignedShort(); + mColor2 = pInput.readUnsignedShort(); + mColor3 = pInput.readUnsignedShort(); + mColor4 = pInput.readUnsignedShort(); + + mOpacity = pInput.readUnsignedShort(); + + mKind = pInput.readUnsignedByte(); + + pInput.readByte(); // Pad + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()); + builder.append("["); + builder.append("color space: 0x").append(Integer.toHexString(mColorSpace)); + builder.append(", colors: [0x").append(Integer.toHexString(mColor1)); + builder.append(", 0x").append(Integer.toHexString(mColor2)); + builder.append(", 0x").append(Integer.toHexString(mColor3)); + builder.append(", 0x").append(Integer.toHexString(mColor4)); + builder.append("], opacity: ").append(mOpacity); + builder.append(", kind: ").append(mKind); + builder.append("]"); + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java new file mode 100755 index 00000000..ca37acfd --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java @@ -0,0 +1,133 @@ +/* + * 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.psd; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.IIOException; +import java.io.IOException; + +/** + * PSDHeader + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$ + */ +class PSDHeader { +// The header is 26 bytes in length and is structured as follows: +// +// typedef struct _PSD_HEADER +// { +// BYTE Signature[4]; /* File ID "8BPS" */ +// WORD Version; /* Version number, always 1 */ +// BYTE Reserved[6]; /* Reserved, must be zeroed */ +// WORD Channels; /* Number of color channels (1-24) including alpha +// channels */ +// LONG Rows; /* Height of image in pixels (1-30000) */ +// LONG Columns; /* Width of image in pixels (1-30000) */ +// WORD Depth; /* Number of bits per channel (1, 8, and 16) */ +// WORD Mode; /* Color mode */ +// } PSD_HEADER; + + final short mChannels; + final int mWidth; + final int mHeight; + final short mBits; + final short mMode; + + PSDHeader(ImageInputStream pInput) throws IOException { + int signature = pInput.readInt(); + if (signature != PSD.SIGNATURE_8BPS) { + throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")"); + } + + int version = pInput.readUnsignedShort(); + if (version != 1) { + if (version == 2) { + throw new IIOException("Large Document Format (PSB) not supported yet."); + } + throw new IIOException("Unknown PSD version, expected 1 or 2: 0x" + Integer.toHexString(version)); + } + + byte[] reserved = new byte[6]; + pInput.readFully(reserved); + + mChannels = pInput.readShort(); + mHeight = pInput.readInt(); // Rows + mWidth = pInput.readInt(); // Coloumns + mBits = pInput.readShort(); + mMode = pInput.readShort(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()); + builder.append("[Channels: "); + builder.append(mChannels); + builder.append(", width: "); + builder.append(mWidth); + builder.append(", height: "); + builder.append(mHeight); + builder.append(", depth: "); + builder.append(mBits); + builder.append(", mode: "); + builder.append(mMode); + switch (mMode) { + case PSD.COLOR_MODE_MONOCHROME: + builder.append(" (Monochrome)"); + break; + case PSD.COLOR_MODE_GRAYSCALE: + builder.append(" (Grayscale)"); + break; + case PSD.COLOR_MODE_INDEXED: + builder.append(" (Indexed)"); + break; + case PSD.COLOR_MODE_RGB: + builder.append(" (RGB)"); + break; + case PSD.COLOR_MODE_CMYK: + builder.append(" (CMYK)"); + break; + case PSD.COLOR_MODE_MULTICHANNEL: + builder.append(" (Multi channel)"); + break; + case PSD.COLOR_MODE_DUOTONE: + builder.append(" (Duotone)"); + break; + case PSD.COLOR_MODE_LAB: + builder.append(" (Lab color)"); + break; + default: + builder.append(" (Unkown mode)"); + } + builder.append("]"); + + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java new file mode 100755 index 00000000..7f5d1792 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java @@ -0,0 +1,575 @@ +/* + * 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.psd; + +import com.twelvemonkeys.image.ImageUtil; +import com.twelvemonkeys.imageio.ImageReaderBase; +import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier; + +import javax.imageio.IIOException; +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.awt.image.*; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * ImageReader for Adobe Photoshop Document format. + * + * @see Adobe Photoshop File Format Summary + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDImageReader.java,v 1.0 Apr 29, 2008 4:45:52 PM haraldk Exp$ + */ +// TODO: Implement AOI and subsampling +// TODO: Implement meta data reading +// TODO: Implement layer reading +// TODO: Allow reading separate (or some?) layers +// TODO: Consider Romain Guy's Java 2D implementation of PS filters for the blending modes in layers +// http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/ +// See http://www.codeproject.com/KB/graphics/PSDParser.aspx +// See http://www.adobeforums.com/webx?14@@.3bc381dc/0 +public class PSDImageReader extends ImageReaderBase { + private PSDHeader mHeader; + private PSDColorData mColorData; + private List mImageResources; + private PSDGlobalLayerMask mGlobalLayerMask; + private List mLayerInfo; + private ICC_ColorSpace mColorSpace; + + protected PSDImageReader(final ImageReaderSpi pOriginatingProvider) { + super(pOriginatingProvider); + } + + protected void resetMembers() { + mHeader = null; + mColorData = null; + mImageResources = null; + mColorSpace = null; + } + + public int getWidth(int pIndex) throws IOException { + checkBounds(pIndex); + readHeader(); + return mHeader.mWidth; + } + + public int getHeight(int pIndex) throws IOException { + checkBounds(pIndex); + readHeader(); + return mHeader.mHeight; + } + + public Iterator getImageTypes(int pIndex) throws IOException { + checkBounds(pIndex); + readHeader(); + + ColorSpace cs; + List types = new ArrayList(); + + switch (mHeader.mMode) { + case PSD.COLOR_MODE_INDEXED: + if (mHeader.mChannels == 1 && mHeader.mBits == 8) { + types.add(IndexedImageTypeSpecifier.createFromIndexColorModel(mColorData.getIndexColorModel())); + } + else { + throw new IIOException("Unsupported channel count/bit depth for Indexed Color PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits"); + } + break; + case PSD.COLOR_MODE_DUOTONE: + // NOTE: Duotone (whatever that is) should be treated as grayscale, so fall-through + case PSD.COLOR_MODE_GRAYSCALE: + if (mHeader.mChannels == 1 && mHeader.mBits == 8) { + types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY)); + } + else if (mHeader.mChannels == 1 && mHeader.mBits == 16) { + types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY)); + } + else { + throw new IIOException("Unsupported channel count/bit depth for Gray Scale PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits"); + } + break; + case PSD.COLOR_MODE_RGB: + cs = getEmbeddedColorSpace(); + if (cs == null) { + cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); + } + + if (mHeader.mChannels == 3 && mHeader.mBits == 8) { +// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)); + types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {2, 1, 0}, DataBuffer.TYPE_BYTE, false, false)); + } + else if (mHeader.mChannels >= 4 && mHeader.mBits == 8) { +// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR)); + types.add(ImageTypeSpecifier.createInterleaved(cs, new int[] {3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false)); + } + else { + throw new IIOException("Unsupported channel count/bit depth for RGB PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits"); + } + break; + case PSD.COLOR_MODE_CMYK: + // TODO: We should convert these to their RGB equivalents while reading for the common-case, + // as Java2D is extremely slow displaying custom images. + // Converting to RGB is also correct behaviour, according to the docs. + // The code below is, however, correct for raw type. + cs = getEmbeddedColorSpace(); + if (cs == null) { + cs = CMYKColorSpace.getInstance(); + } + + if (mHeader.mChannels == 4 && mHeader.mBits == 8) { +// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR)); + types.add(ImageTypeSpecifier.createInterleaved(cs, new int[]{3, 2, 1, 0}, DataBuffer.TYPE_BYTE, false, false)); + } + else if (mHeader.mChannels == 5 && mHeader.mBits == 8) { +// types.add(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR)); + types.add(ImageTypeSpecifier.createInterleaved(cs, new int[]{4, 3, 2, 1, 0}, DataBuffer.TYPE_BYTE, true, false)); + } + else { + throw new IIOException("Unsupported channel count/bit depth for CMYK PSD: " + mHeader.mChannels + " channels/" + mHeader.mBits + " bits"); + } + break; + default: + throw new IIOException("Unsupported PSD MODE: " + mHeader.mMode); + } + + return types.iterator(); + } + + private ColorSpace getEmbeddedColorSpace() throws IOException { + readImageResources(true); + // TODO: Skip this, requires storing some stream offsets + readLayerAndMaskInfo(false); + + if (mColorSpace == null) { + ICC_Profile profile = null; + for (PSDImageResource resource : mImageResources) { + if (resource instanceof ICCProfile) { + profile = ((ICCProfile) resource).getProfile(); + break; + } + } + + mColorSpace = profile == null ? null : new ICC_ColorSpace(profile); + } + + return mColorSpace; + } + + // TODO: Implement param handling + public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException { + checkBounds(pIndex); + + readHeader(); + + processImageStarted(pIndex); + + readImageResources(false); + readLayerAndMaskInfo(false); + + BufferedImage image = getDestination(pParam, getImageTypes(pIndex), mHeader.mWidth, mHeader.mHeight); + // TODO: Should do color convert op for CMYK -> RGB + ColorModel cm = image.getColorModel(); + final boolean isCMYK = cm.getColorSpace().getType() == ColorSpace.TYPE_CMYK; + final int numColorComponents = cm.getColorSpace().getNumComponents(); + + WritableRaster raster = image.getRaster(); + if (!(raster.getDataBuffer() instanceof DataBufferByte)) { + throw new IIOException("Unsupported raster type: " + raster); + } + byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData(); + + // TODO: Maybe a banded raster would be easier than interleaved? + final int channels = raster.getNumBands(); + +// System.out.println("channels: " + channels); +// System.out.println("numColorComponents: " + numColorComponents); +// System.out.println("isCMYK: " + isCMYK); + + short compression = mImageInput.readShort(); + + // TODO: Bitmap (depth = 1) and 16 bit (depth = 16) must be read differently, obviously... + // This code works fine for images with channel depth = 8 + switch (compression) { + case PSD.COMPRESSION_NONE: + // TODO: This entire reading block is duplicated and should be replaced with the one for RLE! +// System.out.println("Uncompressed"); + for (int c = 0; c < mHeader.mChannels; c++) { + for (int y = 0; y < mHeader.mHeight; y++) { + for (int x = 0; x < mHeader.mWidth; x++) { + int offset = (x + y * mHeader.mWidth) * channels; + + byte value = mImageInput.readByte(); + + // CMYK values are stored inverted, but alpha is not + if (isCMYK && c < numColorComponents) { + value = (byte) (255 - value & 0xff); + } + +// System.out.println("b: " + Integer.toHexString(b & 0xff)); + data[offset + (channels - 1 - c)] = value; + } + + if (abortRequested()) { + break; + } + processImageProgress((c * y * 100) / mHeader.mChannels * mHeader.mHeight); + } + if (abortRequested()) { + break; + } + } + break; + case PSD.COMPRESSION_RLE: +// System.out.println("PackBits compressed"); + // NOTE: Offsets will allow us to easily skip rows before AOI + int[] offsets = new int[mHeader.mChannels * mHeader.mHeight]; + for (int i = 0; i < offsets.length; i++) { + offsets[i] = mImageInput.readUnsignedShort(); + } + + int x = 0, y = 0, c = 0; + try { + for (c = 0; c < channels; c++) { + for (y = 0; y < mHeader.mHeight; y++) { + int length = offsets[c * mHeader.mHeight + y]; +// System.out.println("channel: " + c + " line: " + y + " length: " + length); + DataInputStream input = PSDUtil.createPackBitsStream(mImageInput, length); + for (x = 0; x < mHeader.mWidth; x++) { + int offset = (x + y * mHeader.mWidth) * channels; + + byte value = input.readByte(); + +// if (c < numColorComponents) { +// continue; +// } + + // CMYK values are stored inverted, but alpha is not + if (isCMYK && c < numColorComponents) { + value = (byte) (255 - value & 0xff); + } + +// System.out.println("b: " + Integer.toHexString(b & 0xff)); + data[offset + (channels - 1 - c)] = value; + } + input.close(); + + if (abortRequested()) { + break; + } + processImageProgress((c * y * 100) / mHeader.mChannels * mHeader.mHeight); + } + if (abortRequested()) { + break; + } + } + } + catch (IOException e) { + System.err.println("c: " + c); + System.err.println("y: " + y); + System.err.println("x: " + x); + throw e; + } + catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + System.out.println("data.length: " + data.length); + System.err.println("c: " + c); + System.err.println("y: " + y); + System.err.println("x: " + x); + throw e; + } + break; + case PSD.COMPRESSION_ZIP: + // TODO: Could probably use the ZIPDecoder (DeflateDecoder) here.. + case PSD.COMPRESSION_ZIP_PREDICTON: + // TODO: Need to find out if the normal java.util.zip can handle this... + // Could be same as PNG prediction? Read up... + throw new IIOException("ZIP compression not supported yet"); + default: + throw new IIOException("Unknown compression type: " + compression); + } + + // Compose out the background of the semi-transparent pixels, as PS somehow has the background composed in + decomposeAlpha(image); + + if (abortRequested()) { + processReadAborted(); + } + else { + processImageComplete(); + } + + return image; + } + + private void decomposeAlpha(final BufferedImage pImage) throws IOException { + ColorModel cm = pImage.getColorModel(); + + // TODO: What about CMYK + alpha? + if (cm.hasAlpha() && cm.getColorSpace().getType() == ColorSpace.TYPE_RGB) { + WritableRaster raster = pImage.getRaster(); + + // TODO: Probably faster to do this inline.. + // TODO: This is not so good, as it might break acceleration... + byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData(); + + final int w = pImage.getWidth(); + final int channels = raster.getNumBands(); + for (int y = 0; y < pImage.getHeight(); y++) { + for (int x = 0; x < w; x++) { + int offset = (x + y * w) * channels; + + // TODO: Is the document background always white!? + // ABGR format + int alpha = data[offset] & 0xff; + if (alpha != 0) { + double normalizedAlpha = alpha / 255.0; + for (int i = 1; i < channels; i++) { + data[offset + i] = decompose(data[offset + i] & 0xff, normalizedAlpha); + } + } + else { + for (int i = 1; i < channels; i++) { + data[offset + i] = 0; + } + } + } + } + + } +// System.out.println("PSDImageReader.coerceData: " + cm.getClass()); +// System.out.println("other.equals(cm): " + (other == cm)); + } + + private static byte decompose(final int pColor, final double pAlpha) { + // Adapted from Computer Graphics: Principles and Practice (Foley et al.), p. 837 + double color = pColor / 255.0; + return (byte) ((color / pAlpha - ((1 - pAlpha) / pAlpha)) * 255); + } + + private void readHeader() throws IOException { + assertInput(); + if (mHeader == null) { + mHeader = new PSDHeader(mImageInput); + + /* + Contains the required data to define the color mode. + + For indexed color images, the count will be equal to 768, and the mode data + will contain the color table for the image, in non-interleaved order. + + For duotone images, the mode data will contain the duotone specification, + the format of which is not documented. Non-Photoshop readers can treat + the duotone image as a grayscale image, and keep the duotone specification + around as a black box for use when saving the file. + */ + if (mHeader.mMode == PSD.COLOR_MODE_INDEXED) { + mColorData = new PSDColorData(mImageInput); + } + else { + // Skip color mode data for other modes + long length = mImageInput.readUnsignedInt(); + mImageInput.skipBytes(length); + } + + // Don't need the header again + mImageInput.flushBefore(mImageInput.getStreamPosition()); + } + } + + private void readImageResources(boolean pParseData) throws IOException { + // TODO: Avoid unnecessary stream repositioning + long pos = mImageInput.getFlushedPosition(); + mImageInput.seek(pos); + + long length = mImageInput.readUnsignedInt(); + + if (pParseData && length > 0) { + if (mImageResources == null) { + mImageResources = new ArrayList(); + long expectedEnd = mImageInput.getStreamPosition() + length; + while (mImageInput.getStreamPosition() < expectedEnd) { + PSDImageResource resource = PSDImageResource.read(mImageInput); + mImageResources.add(resource); + } + + if (mImageInput.getStreamPosition() != expectedEnd) { + throw new IIOException("Corrupt PSD document"); + } + } + } + + mImageInput.seek(pos + length + 4); + } + + private void readLayerAndMaskInfo(boolean pParseData) throws IOException { + // TODO: Make sure we are positioned correctly + long length = mImageInput.readUnsignedInt(); + if (pParseData && length > 0) { + long pos = mImageInput.getStreamPosition(); + + long layerInfoLength = mImageInput.readUnsignedInt(); + + /* + "Layer count. If it is a negative number, its absolute value is the number of + layers and the first alpha channel contains the transparency data for the + merged result." + */ + // TODO: Figure out what the last part of that sentence means in practice... + int layers = mImageInput.readShort(); +// System.out.println("layers: " + layers); + + PSDLayerInfo[] layerInfo = new PSDLayerInfo[Math.abs(layers)]; + for (int i = 0; i < layerInfo.length; i++) { + layerInfo[i] = new PSDLayerInfo(mImageInput); +// System.out.println("layerInfo[" + i + "]: " + layerInfo[i]); + } + mLayerInfo = Arrays.asList(layerInfo); + + for (PSDLayerInfo info : layerInfo) { + for (PSDChannelInfo channelInfo : info.mChannelInfo) { + int compression = mImageInput.readUnsignedShort(); + // 0: None, 1: PackBits RLE, 2: Zip, 3: Zip w/prediction + switch (compression) { + case PSD.COMPRESSION_NONE: +// System.out.println("Compression: None"); + break; + case PSD.COMPRESSION_RLE: +// System.out.println("Compression: PackBits RLE"); + break; + case PSD.COMPRESSION_ZIP: +// System.out.println("Compression: ZIP"); + break; + case PSD.COMPRESSION_ZIP_PREDICTON: +// System.out.println("Compression: ZIP with prediction"); + break; + default: + // TODO: Do we care, as we can just skip the data? + // We could issue a warning to the warning listener + throw new IIOException(String.format( + "Unknown PSD compression: %d. Expected 0 (none), 1 (RLE), 2 (ZIP) or 3 (ZIP w/prediction).", + compression + )); + } + + // TODO: If RLE, the the image data starts with the byte counts + // for all the scan lines in the channel (LayerBottom*LayerTop), with + // each count stored as a two*byte value. + // if (compression == 1) { + // mImageInput.skipBytes(channelInfo.mLength); + // } + + // TODO: Read channel image data (same format as composite image channel data) + mImageInput.skipBytes(channelInfo.mLength - 2); + // if (channelInfo.mLength % 2 != 0) { + // mImageInput.readByte(); + // } + } + } + + // TODO: We seem to have some alignment issues here... + // I'm always reading two bytes off.. + + long read = mImageInput.getStreamPosition() - pos; +// System.out.println("layerInfoLength: " + layerInfoLength); +// System.out.println("layer info read: " + (read - 4)); // - 4 for the layerInfoLength field itself + long diff = layerInfoLength - (read - 4); +// System.out.println("diff: " + diff); + mImageInput.skipBytes(diff); + + // TODO: Global LayerMaskInfo (18 bytes or more..?) + // 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad) + long layerMaskInfoLength = mImageInput.readUnsignedInt(); +// System.out.println("GlobalLayerMaskInfo length: " + layerMaskInfoLength); + if (layerMaskInfoLength > 0) { + mGlobalLayerMask = new PSDGlobalLayerMask(mImageInput); + } + + read = mImageInput.getStreamPosition() - pos; + + long toSkip = length - read; +// System.out.println("toSkip: " + toSkip); + mImageInput.skipBytes(toSkip); + } + else { + mImageInput.skipBytes(length); + } + } + + public static void main(String[] pArgs) throws IOException { + PSDImageReader imageReader = new PSDImageReader(null); + + File file = new File(pArgs[0]); + ImageInputStream stream = ImageIO.createImageInputStream(file); + imageReader.setInput(stream); + imageReader.readHeader(); + System.out.println("imageReader.mHeader: " + imageReader.mHeader); + + imageReader.readImageResources(true); + System.out.println("imageReader.mImageResources: " + imageReader.mImageResources); + + imageReader.readLayerAndMaskInfo(true); + System.out.println("imageReader.mLayerInfo: " + imageReader.mLayerInfo); + System.out.println("imageReader.mGlobalLayerMask: " + imageReader.mGlobalLayerMask); + + long start = System.currentTimeMillis(); + ImageReadParam param = new ImageReadParam(); +// param.setSourceRegion(new Rectangle(100, 100, 300, 200)); + BufferedImage image = imageReader.read(0, param); + System.out.println("time: " + (System.currentTimeMillis() - start)); + System.out.println("image: " + image); + + if (image.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK) { + try { + ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null); + image = op.filter(image, new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR_PRE)); + } + catch (Exception e) { + e.printStackTrace(); + image = ImageUtil.accelerate(image); + } + System.out.println("time: " + (System.currentTimeMillis() - start)); + System.out.println("image: " + image); + } + + showIt(image, file.getName()); + } + +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderSpi.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderSpi.java new file mode 100755 index 00000000..79117fe1 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderSpi.java @@ -0,0 +1,92 @@ +/* + * 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.psd; + +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.ImageReader; +import java.io.IOException; +import java.util.Locale; + +/** + * PSDImageReaderSpi + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDImageReaderSpi.java,v 1.0 Apr 29, 2008 4:49:03 PM haraldk Exp$ + */ +public class PSDImageReaderSpi extends ImageReaderSpi { + + /** + * Creates an PSDImageReaderSpi + */ + public PSDImageReaderSpi() { + super( + "TwelveMonkeys", + "2.0", + new String[]{"psd", "PSD"}, + new String[]{"psd"}, + new String[]{ + "application/vnd.adobe.photoshop", // This one seems official, used in XMP + "image/x-psd", "application/x-photoshop", "image/x-photoshop" + }, + "com.twelvemkonkeys.imageio.plugins.psd.PSDImageReader", + STANDARD_INPUT_TYPE, +// new String[]{"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"}, + null, + true, null, null, null, null, + true, null, null, null, null + ); + } + + public boolean canDecodeInput(Object pSource) throws IOException { + if (!(pSource instanceof ImageInputStream)) { + return false; + } + + ImageInputStream stream = (ImageInputStream) pSource; + + stream.mark(); + try { + return stream.readInt() == PSD.SIGNATURE_8BPS; + // TODO: Test more of the header, see PSDImageReader#readHeader + } + finally { + stream.reset(); + } + } + + public ImageReader createReaderInstance(Object pExtension) throws IOException { + return new PSDImageReader(this); + } + + public String getDescription(Locale pLocale) { + return "Adobe Photoshop Document (PSD) image reader"; + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageResource.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageResource.java new file mode 100755 index 00000000..c5484460 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageResource.java @@ -0,0 +1,168 @@ +/* + * 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.psd; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.IIOException; +import java.io.IOException; +import java.lang.reflect.Field; + +/** + * PSDImageResource + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDImageResource.java,v 1.0 Apr 29, 2008 5:49:06 PM haraldk Exp$ + */ +class PSDImageResource { + final short mId; + final String mName; + final long mSize; + + PSDImageResource(final short pId, final ImageInputStream pInput) throws IOException { + mId = pId; + + mName = PSDUtil.readPascalString(pInput); + + mSize = pInput.readUnsignedInt(); + readData(pInput); + // Data is even-padded + if (mSize % 2 != 0) { + pInput.read(); + } + } + + /** + * This default implementation simply skips the data. + * + * @param pInput the input + * @throws IOException if an I/O exception occurs + */ + protected void readData(final ImageInputStream pInput) throws IOException { + // TODO: This design is ugly, as subclasses readData is invoked BEFORE their respective constructor... + pInput.skipBytes(mSize); + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", data length: "); + builder.append(mSize); + builder.append("]"); + + return builder.toString(); + } + + protected StringBuilder toStringBuilder() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()); + + builder.append(resourceTypeForId(mId)); + + builder.append("[ID: 0x"); + builder.append(Integer.toHexString(mId)); + if (mName != null && mName.trim().length() != 0) { + builder.append(", name: \""); + builder.append(mName); + builder.append("\""); + } + + return builder; + } + + static String resourceTypeForId(final short pId) { + switch (pId) { + case PSD.RES_RESOLUTION_INFO: + case PSD.RES_ALPHA_CHANNEL_INFO: + case PSD.RES_DISPLAY_INFO: + case PSD.RES_PRINT_FLAGS: + case PSD.RES_THUMBNAIL_PS4: + case PSD.RES_THUMBNAIL: + case PSD.RES_ICC_PROFILE: + case PSD.RES_EXIF_DATA_1: +// case PSD.RES_EXIF_DATA_3: + case PSD.RES_XMP_DATA: + case PSD.RES_PRINT_FLAGS_INFORMATION: + return ""; + default: + try { + for (Field field : PSD.class.getDeclaredFields()) { + if (field.getName().startsWith("RES_") && field.getInt(null) == pId) { + return "(" + field.getName().substring(4) + ")"; + } + } + } + catch (IllegalAccessException ignore) { + } + + return "(unknown resource)"; + } + } + + public static PSDImageResource read(final ImageInputStream pInput) throws IOException { + int type = pInput.readInt(); + if (type != PSD.RESOURCE_TYPE) { + throw new IIOException("Wrong image resource type, expected 8BIM: " + PSDUtil.intToStr(type)); + } + + // TODO: Process more of the resource stuff, most important are IPTC, EXIF and XMP data, + // version info, and thumbnail for thumbnail-support. + short id = pInput.readShort(); + switch (id) { + case PSD.RES_RESOLUTION_INFO: + return new PSDResolutionInfo(id, pInput); + case PSD.RES_ALPHA_CHANNEL_INFO: + return new PSDAlphaChannelInfo(id, pInput); + case PSD.RES_DISPLAY_INFO: + return new PSDDisplayInfo(id, pInput); + case PSD.RES_PRINT_FLAGS: + return new PSDPrintFlags(id, pInput); + case PSD.RES_THUMBNAIL_PS4: + case PSD.RES_THUMBNAIL: + return new PSDThumbnail(id, pInput); + case PSD.RES_ICC_PROFILE: + return new ICCProfile(id, pInput); + case PSD.RES_EXIF_DATA_1: + return new PSDEXIF1Data(id, pInput); + case PSD.RES_XMP_DATA: + return new PSDXMPData(id, pInput); + case PSD.RES_PRINT_FLAGS_INFORMATION: + return new PSDPrintFlagsInformation(id, pInput); + default: + if (id >= 0x07d0 && id <= 0x0bb6) { + // TODO: Parse saved path information + return new PSDImageResource(id, pInput); + } + else { + return new PSDImageResource(id, pInput); + } + } + + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerBlendMode.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerBlendMode.java new file mode 100755 index 00000000..926be1c9 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerBlendMode.java @@ -0,0 +1,117 @@ +/* + * 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.psd; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.IIOException; +import java.io.IOException; + +/** + * PSDLayerBlendMode + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDLayerBlendMode.java,v 1.0 May 8, 2008 4:34:35 PM haraldk Exp$ + */ +class PSDLayerBlendMode { + final int mBlendMode; + final int mOpacity; // 0-255 + final int mClipping; // 0: base, 1: non-base + final int mFlags; + + public PSDLayerBlendMode(ImageInputStream pInput) throws IOException { + int blendModeSig = pInput.readInt(); + if (blendModeSig != PSD.RESOURCE_TYPE) { // TODO: Is this really just a resource? + throw new IIOException("Illegal PSD Blend Mode signature, expected 8BIM: " + PSDUtil.intToStr(blendModeSig)); + } + + mBlendMode = pInput.readInt(); + + mOpacity = pInput.readUnsignedByte(); + mClipping = pInput.readUnsignedByte(); + mFlags = pInput.readUnsignedByte(); + + pInput.readByte(); // Pad + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()); + + builder.append("["); + builder.append("mode: \"").append(PSDUtil.intToStr(mBlendMode)); + builder.append("\", opacity: ").append(mOpacity); + builder.append(", clipping: ").append(mClipping); + builder.append(", flags: ").append(byteToBinary(mFlags)); + + // TODO: Maybe the flag bits have oposite order? + builder.append(" ("); + if ((mFlags & 0x01) != 0) { + builder.append("Transp. protected "); + } + else { + builder.append("Transp. open"); + } + if ((mFlags & 0x02) != 0) { + builder.append(", Visible"); + } + else { + builder.append(", Hidden"); + } + if ((mFlags & 0x04) != 0) { + builder.append(", Obsolete bit"); + } + if ((mFlags & 0x08) != 0) { + builder.append(", Photoshop 5 data"); + } + if ((mFlags & 0x10) != 0) { + builder.append(", Pixel data irrelevant"); + } + if ((mFlags & 0x20) != 0) { + builder.append(", Unknown bit 5"); + } + if ((mFlags & 0x40) != 0) { + builder.append(", Unknown bit 6"); + } + if ((mFlags & 0x80) != 0) { + builder.append(", Unknown bit 7"); + } + builder.append(")"); + + builder.append("]"); + + return builder.toString(); + } + + private static String byteToBinary(final int pFlags) { + String flagStr = Integer.toBinaryString(pFlags); + flagStr = "00000000".substring(flagStr.length()) + flagStr; + return flagStr; + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java new file mode 100755 index 00000000..3f306158 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java @@ -0,0 +1,133 @@ +/* + * 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.psd; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.IIOException; +import java.io.IOException; +import java.util.Arrays; + +/** + * PSDLayerInfo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDLayerInfo.java,v 1.0 Apr 29, 2008 6:01:12 PM haraldk Exp$ + */ +class PSDLayerInfo { + private int mTop; + private int mLeft; + private int mBottom; + private int mRight; + + PSDChannelInfo[] mChannelInfo; + private PSDLayerBlendMode mBlendMode; + private PSDLayerMaskData mLayerMaskData; + private PSDChannelSourceDestinationRange[] mRanges; + private String mLayerName; + + PSDLayerInfo(ImageInputStream pInput) throws IOException { + mTop = pInput.readInt(); + mLeft = pInput.readInt(); + mBottom = pInput.readInt(); + mRight = pInput.readInt(); + + int channels = pInput.readUnsignedShort(); + + mChannelInfo = new PSDChannelInfo[channels]; + for (int i = 0; i < channels; i++) { + short channelId = pInput.readShort(); + long length = pInput.readUnsignedInt(); + + mChannelInfo[i] = new PSDChannelInfo(channelId, length); + } + + mBlendMode = new PSDLayerBlendMode(pInput); + + // Lenght of layer mask data + long extraDataSize = pInput.readUnsignedInt(); + // TODO: Allow skipping the rest here? + // pInput.skipBytes(extraDataSize); + + // Layer mask/adjustment layer data + int layerMaskDataSize = pInput.readInt(); // May be 0, 20 or 36 bytes... + if (layerMaskDataSize != 0) { + mLayerMaskData = new PSDLayerMaskData(pInput, layerMaskDataSize); + } + + int layerBlendingDataSize = pInput.readInt(); + if (layerBlendingDataSize % 8 != 0) { + throw new IIOException("Illegal PSD Layer Blending Data size: " + layerBlendingDataSize + ", expected multiple of 8"); + } + + mRanges = new PSDChannelSourceDestinationRange[layerBlendingDataSize / 8]; + for (int i = 0; i < mRanges.length; i++) { + mRanges[i] = new PSDChannelSourceDestinationRange(pInput, (i == 0 ? "Gray" : "Channel " + (i - 1))); + } + + + mLayerName = PSDUtil.readPascalString(pInput); + + int layerNameSize = mLayerName.length() + 1; + // readPascalString has already read pad byte for word alignment + if (layerNameSize % 2 != 0) { + layerNameSize++; + } + // Skip two more pad bytes if needed + if (layerNameSize % 4 != 0) { + pInput.skipBytes(2); + layerNameSize += 2; + } + + // TODO: There's some data skipped here... + // Adjustment layer info etc... + pInput.skipBytes(extraDataSize - layerMaskDataSize - 4 - layerBlendingDataSize - 4 - layerNameSize); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()); + builder.append("["); + builder.append("top: ").append(mTop); + builder.append(", left: ").append(mLeft); + builder.append(", bottom: ").append(mBottom); + builder.append(", right: ").append(mRight); + + builder.append(", channels: ").append(Arrays.toString(mChannelInfo)); + builder.append(", blend mode: ").append(mBlendMode); + if (mLayerMaskData != null) { + builder.append(", layer mask data: ").append(mLayerMaskData); + } + builder.append(", ranges: ").append(Arrays.toString(mRanges)); + builder.append(", layer name: \"").append(mLayerName).append("\""); + + builder.append("]"); + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerMaskData.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerMaskData.java new file mode 100755 index 00000000..277938b6 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerMaskData.java @@ -0,0 +1,136 @@ +/* + * 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.psd; + +import javax.imageio.IIOException; +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * PSDLayerMaskData + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDLayerMaskData.java,v 1.0 May 6, 2008 5:15:05 PM haraldk Exp$ + */ +class PSDLayerMaskData { + private int mTop; + private int mLeft; + private int mBottom; + private int mRight; + private int mDefaultColor; + private int mFlags; + + private boolean mLarge; + private int mRealFlags; + private int mRealUserBackground; + private int mRealTop; + private int mRealLeft; + private int mRealBottom; + private int mRealRight; + + PSDLayerMaskData(ImageInputStream pInput, int pSize) throws IOException { + if (pSize != 20 && pSize != 36) { + throw new IIOException("Illegal PSD Layer Mask data size: " + pSize + " (expeced 20 or 36)"); + } + mTop = pInput.readInt(); + mLeft = pInput.readInt(); + mBottom = pInput.readInt(); + mRight = pInput.readInt(); + + mDefaultColor = pInput.readUnsignedByte(); + + mFlags = pInput.readUnsignedByte(); + + if (pSize == 20) { + pInput.readShort(); // Pad + } + else { + // TODO: What to make out of this? + mLarge = true; + + mRealFlags = pInput.readUnsignedByte(); + mRealUserBackground = pInput.readUnsignedByte(); + + mRealTop = pInput.readInt(); + mRealLeft = pInput.readInt(); + mRealBottom = pInput.readInt(); + mRealRight = pInput.readInt(); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(getClass().getSimpleName()); + builder.append("["); + builder.append("top: ").append(mTop); + builder.append(", left: ").append(mLeft); + builder.append(", bottom: ").append(mBottom); + builder.append(", right: ").append(mRight); + builder.append(", default color: ").append(mDefaultColor); + builder.append(", flags: ").append(Integer.toBinaryString(mFlags)); + + // TODO: Maybe the flag bits have oposite order? + builder.append(" ("); + if ((mFlags & 0x01) != 0) { + builder.append("Pos. rel. to layer"); + } + else { + builder.append("Pos. abs."); + } + if ((mFlags & 0x02) != 0) { + builder.append(", Mask disabled"); + } + else { + builder.append(", Mask enabled"); + } + if ((mFlags & 0x04) != 0) { + builder.append(", Invert mask"); + } + if ((mFlags & 0x08) != 0) { + builder.append(", Unknown bit 3"); + } + if ((mFlags & 0x10) != 0) { + builder.append(", Unknown bit 4"); + } + if ((mFlags & 0x20) != 0) { + builder.append(", Unknown bit 5"); + } + if ((mFlags & 0x40) != 0) { + builder.append(", Unknown bit 6"); + } + if ((mFlags & 0x80) != 0) { + builder.append(", Unknown bit 7"); + } + builder.append(")"); + + builder.append("]"); + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlags.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlags.java new file mode 100755 index 00000000..0cb3ddca --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlags.java @@ -0,0 +1,58 @@ +package com.twelvemonkeys.imageio.plugins.psd; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * PSDPrintFlagsInfo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDPrintFlagsInfo.java,v 1.0 Jul 28, 2009 5:16:27 PM haraldk Exp$ + */ +final class PSDPrintFlags extends PSDImageResource { + private boolean mLabels; + private boolean mCropMasks; + private boolean mColorBars; + private boolean mRegistrationMarks; + private boolean mNegative; + private boolean mFlip; + private boolean mInterpolate; + private boolean mCaption; + + PSDPrintFlags(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(final ImageInputStream pInput) throws IOException { + mLabels = pInput.readUnsignedByte() != 0; + mCropMasks = pInput.readUnsignedByte() != 0; + mColorBars = pInput.readUnsignedByte() != 0; + mRegistrationMarks = pInput.readUnsignedByte() != 0; + mNegative = pInput.readUnsignedByte() != 0; + mFlip = pInput.readUnsignedByte() != 0; + mInterpolate = pInput.readUnsignedByte() != 0; + mCaption = pInput.readUnsignedByte() != 0; + + pInput.readUnsignedByte(); // Pad + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", labels: ").append(mLabels); + builder.append(", crop masks: ").append(mCropMasks); + builder.append(", color bars: ").append(mColorBars); + builder.append(", registration marks: ").append(mRegistrationMarks); + builder.append(", negative: ").append(mNegative); + builder.append(", flip: ").append(mFlip); + builder.append(", interpolate: ").append(mInterpolate); + builder.append(", caption: ").append(mCaption); + + builder.append("]"); + + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlagsInformation.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlagsInformation.java new file mode 100755 index 00000000..80583db7 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDPrintFlagsInformation.java @@ -0,0 +1,47 @@ +package com.twelvemonkeys.imageio.plugins.psd; + +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; + +/** + * PSDPrintFlagsInfo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDPrintFlagsInfo.java,v 1.0 Jul 28, 2009 5:16:27 PM haraldk Exp$ + */ +final class PSDPrintFlagsInformation extends PSDImageResource { + private int mVersion; + private boolean mCropMasks; + private int mField; + private long mBleedWidth; + private int mBleedScale; + + PSDPrintFlagsInformation(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(final ImageInputStream pInput) throws IOException { + mVersion = pInput.readUnsignedShort(); + mCropMasks = pInput.readUnsignedByte() != 0; + mField = pInput.readUnsignedByte(); + mBleedWidth = pInput.readUnsignedInt(); + mBleedScale = pInput.readUnsignedShort(); + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", version: ").append(mVersion); + builder.append(", crop masks: ").append(mCropMasks); + builder.append(", field: ").append(mField); + builder.append(", bleed width: ").append(mBleedWidth); + builder.append(", bleed scale: ").append(mBleedScale); + + builder.append("]"); + + return builder.toString(); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDReader.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDReader.java new file mode 100755 index 00000000..b38c6480 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDReader.java @@ -0,0 +1,555 @@ +package com.twelvemonkeys.imageio.plugins.psd; + +import java.awt.*; +import java.awt.image.*; +import java.net.*; +import java.io.*; + +/** + * Class PSDReader - Decodes a PhotoShop (.psd) file into one or more frames. + * Supports uncompressed or RLE-compressed RGB files only. Each layer may be + * retrieved as a full frame BufferedImage, or as a smaller image with an + * offset if the layer does not occupy the full frame size. Transparency + * in the original psd file is preserved in the returned BufferedImage's. + * Does not support additional features in PS versions higher than 3.0. + * Example: + *

+ *    PSDReader r = new PSDReader();
+ *    r.readData("sample.psd");
+ *    int n = r.getFrameCount();
+ *    for (int i = 0; i < n; i++) {
+ *       BufferedImage image = r.getLayer(i);
+ *       Point offset = r.getLayerOffset(i);
+ *       // do something with image
+ *    }
+ * 
+ * No copyright asserted on the source code of this class. May be used for + * any purpose. Please forward any corrections to kweiner@fmsware.com. + * + * @author Kevin Weiner, FM Software. + * @version 1.1 January 2004 [bug fix; add RLE support] + * + */ +// SEE: http://www.fileformat.info/format/psd/egff.htm#ADOBEPHO-DMYID.4 +class PSDReader { + + /** + * File readData status: No errors. + */ + public static final int STATUS_OK = 0; + + /** + * File readData status: Error decoding file (may be partially decoded) + */ + public static final int STATUS_FORMAT_ERROR = 1; + + /** + * File readData status: Unable to open source. + */ + public static final int STATUS_OPEN_ERROR = 2; + + /** + * File readData status: Unsupported format + */ + public static final int STATUS_UNSUPPORTED = 3; + + public static int ImageType = BufferedImage.TYPE_INT_ARGB; + + protected BufferedInputStream input; + protected int frameCount; + protected BufferedImage[] frames; + protected int status = 0; + protected int nChan; + protected int width; + protected int height; + protected int nLayers; + protected int miscLen; + protected boolean hasLayers; + protected LayerInfo[] layers; + protected short[] lineLengths; + protected int lineIndex; + protected boolean rleEncoded; + + protected class LayerInfo { + int x, y, w, h; + int nChan; + int[] chanID; + int alpha; + } + + /** + * Gets the number of layers readData from file. + * @return frame count + */ + public int getFrameCount() { + return frameCount; + } + + protected void setInput(InputStream stream) { + // open input stream + init(); + if (stream == null) { + status = STATUS_OPEN_ERROR; + } else { + if (stream instanceof BufferedInputStream) + input = (BufferedInputStream) stream; + else + input = new BufferedInputStream(stream); + } + } + + protected void setInput(String name) { + // open input file + init(); + try { + name = name.trim(); + if (name.startsWith("file:")) { + name = name.substring(5); + while (name.startsWith("/")) + name = name.substring(1); + } + if (name.indexOf("://") > 0) { + URL url = new URL(name); + input = new BufferedInputStream(url.openStream()); + } else { + input = new BufferedInputStream(new FileInputStream(name)); + } + } catch (IOException e) { + status = STATUS_OPEN_ERROR; + } + } + + /** + * Gets display duration for specified frame. Always returns 0. + * + */ + public int getDelay(int forFrame) { + return 0; + } + + /** + * Gets the image contents of frame n. Note that this expands the image + * to the full frame size (if the layer was smaller) and any subsequent + * use of getLayer() will return the full image. + * + * @return BufferedImage representation of frame, or null if n is invalid. + */ + public BufferedImage getFrame(int n) { + BufferedImage im = null; + if ((n >= 0) && (n < nLayers)) { + im = frames[n]; + LayerInfo info = layers[n]; + if ((info.w != width) || (info.h != height)) { + BufferedImage temp = + new BufferedImage(width, height, ImageType); + Graphics2D gc = temp.createGraphics(); + gc.drawImage(im, info.x, info.y, null); + gc.dispose(); + im = temp; + frames[n] = im; + } + } + return im; + } + + /** + * Gets maximum image size. Individual layers may be smaller. + * + * @return maximum image dimensions + */ + public Dimension getFrameSize() { + return new Dimension(width, height); + } + + /** + * Gets the first (or only) image readData. + * + * @return BufferedImage containing first frame, or null if none. + */ + public BufferedImage getImage() { + return getFrame(0); + } + + /** + * Gets the image contents of layer n. May be smaller than full frame + * size - use getFrameOffset() to obtain position of subimage within + * main image area. + * + * @return BufferedImage representation of layer, or null if n is invalid. + */ + public BufferedImage getLayer(int n) { + BufferedImage im = null; + if ((n >= 0) && (n < nLayers)) { + im = frames[n]; + } + return im; + } + + /** + * Gets the subimage offset of layer n if it is smaller than the + * full frame size. + * + * @return Point indicating offset from upper left corner of frame. + */ + public Point getLayerOffset(int n) { + Point p = null; + if ((n >= 0) && (n < nLayers)) { + int x = layers[n].x; + int y = layers[n].y; + p = new Point(x, y); + } + if (p == null) { + p = new Point(0, 0); + } + return p; + } + + /** + * Reads PhotoShop layers from stream. + * + * @param InputStream in PhotoShop format. + * @return readData status code (0 = no errors) + */ + public int read(InputStream stream) { + setInput(stream); + process(); + return status; + } + + /** + * Reads PhotoShop file from specified source (file or URL string) + * + * @param name String containing source + * @return readData status code (0 = no errors) + */ + public int read(String name) { + setInput(name); + process(); + return status; + } + + /** + * Closes input stream and discards contents of all frames. + * + */ + public void reset() { + init(); + } + + protected void close() { + if (input != null) { + try { + input.close(); + } catch (Exception e) {} + input = null; + } + } + protected boolean err() { + return status != STATUS_OK; + } + + protected byte[] fillBytes(int size, int value) { + // create byte array filled with given value + byte[] b = new byte[size]; + if (value != 0) { + byte v = (byte) value; + for (int i = 0; i < size; i++) { + b[i] = v; + } + } + return b; + } + + protected void init() { + close(); + frameCount = 0; + frames = null; + layers = null; + hasLayers = true; + status = STATUS_OK; + } + + protected void makeDummyLayer() { + // creat dummy layer for non-layered image + rleEncoded = readShort() == 1; + hasLayers = false; + nLayers = 1; + layers = new LayerInfo[1]; + LayerInfo layer = new LayerInfo(); + layers[0] = layer; + layer.h = height; + layer.w = width; + int nc = Math.min(nChan, 4); + if (rleEncoded) { + // get list of rle encoded line lengths for all channels + readLineLengths(height * nc); + } + layer.nChan = nc; + layer.chanID = new int[nc]; + for (int i = 0; i < nc; i++) { + int id = i; + if (i == 3) id = -1; + layer.chanID[i] = id; + } + } + + protected void readLineLengths(int nLines) { + // readData list of rle encoded line lengths + lineLengths = new short[nLines]; + for (int i = 0; i < nLines; i++) { + lineLengths[i] = readShort(); + } + lineIndex = 0; + } + + protected BufferedImage makeImage(int w, int h, byte[] r, byte[] g, byte[] b, byte[] a) { + // create image from given plane data + BufferedImage im = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + int[] data = ((DataBufferInt) im.getRaster().getDataBuffer()).getData(); + int n = w * h; + int j = 0; + while (j < n) { + try { + int ac = a[j] & 0xff; + int rc = r[j] & 0xff; + int gc = g[j] & 0xff; + int bc = b[j] & 0xff; + data[j] = (((((ac << 8) | rc) << 8) | gc) << 8) | bc; + } catch (Exception e) {} + j++; + } + return im; + } + + protected void process() { + // decode PSD file + if (err()) return; + readHeader(); + if (err()) return; + readLayerInfo(); + if (err()) return; + if (nLayers == 0) { + makeDummyLayer(); + if (err()) return; + } + readLayers(); + } + + protected int readByte() { + // readData single byte from input + int curByte = 0; + try { + curByte = input.read(); + } catch (IOException e) { + status = STATUS_FORMAT_ERROR; + } + return curByte; + } + + protected int readBytes(byte[] bytes, int n) { + // readData multiple bytes from input + if (bytes == null) return 0; + int r = 0; + try { + r = input.read(bytes, 0, n); + } catch (IOException e) { + status = STATUS_FORMAT_ERROR; + } + if (r < n) { + status = STATUS_FORMAT_ERROR; + } + return r; + } + + protected void readHeader() { + // readData PSD header info + String sig = readString(4); + int ver = readShort(); + skipBytes(6); + nChan = readShort(); + height = readInt(); + width = readInt(); + int depth = readShort(); + int mode = readShort(); + int cmLen = readInt(); + skipBytes(cmLen); + int imResLen = readInt(); + skipBytes(imResLen); + + // require 8-bit RGB data + if ((!sig.equals("8BPS")) || (ver != 1)) { + status = STATUS_FORMAT_ERROR; + } else if ((depth != 8) || (mode != 3)) { + status = STATUS_UNSUPPORTED; + } + } + + protected int readInt() { + // readData big-endian 32-bit integer + return (((((readByte() << 8) | readByte()) << 8) | readByte()) << 8) + | readByte(); + } + + protected void readLayerInfo() { + // readData layer header info + miscLen = readInt(); + if (miscLen == 0) { + return; // no layers, only base image + } + int layerInfoLen = readInt(); + nLayers = readShort(); + if (nLayers > 0) { + layers = new LayerInfo[nLayers]; + } + for (int i = 0; i < nLayers; i++) { + LayerInfo info = new LayerInfo(); + layers[i] = info; + info.y = readInt(); + info.x = readInt(); + info.h = readInt() - info.y; + info.w = readInt() - info.x; + info.nChan = readShort(); + info.chanID = new int[info.nChan]; + for (int j = 0; j < info.nChan; j++) { + int id = readShort(); + int size = readInt(); + info.chanID[j] = id; + } + String s = readString(4); + if (!s.equals("8BIM")) { + status = STATUS_FORMAT_ERROR; + return; + } + skipBytes(4); // blend mode + info.alpha = readByte(); + int clipping = readByte(); + int flags = readByte(); + readByte(); // filler + int extraSize = readInt(); + skipBytes(extraSize); + } + } + + protected void readLayers() { + // readData and convert each layer to BufferedImage + frameCount = nLayers; + frames = new BufferedImage[nLayers]; + for (int i = 0; i < nLayers; i++) { + LayerInfo info = layers[i]; + byte[] r = null, g = null, b = null, a = null; + for (int j = 0; j < info.nChan; j++) { + int id = info.chanID[j]; + switch (id) { + case 0 : r = readPlane(info.w, info.h); break; + case 1 : g = readPlane(info.w, info.h); break; + case 2 : b = readPlane(info.w, info.h); break; + case -1 : a = readPlane(info.w, info.h); break; + default : readPlane(info.w, info.h); + } + if (err()) break; + } + if (err()) break; + int n = info.w * info.h; + if (r == null) r = fillBytes(n, 0); + if (g == null) g = fillBytes(n, 0); + if (b == null) b = fillBytes(n, 0); + if (a == null) a = fillBytes(n, 255); + + BufferedImage im = makeImage(info.w, info.h, r, g, b, a); + frames[i] = im; + } + lineLengths = null; + if ((miscLen > 0) && !err()) { + int n = readInt(); // global layer mask info len + skipBytes(n); + } + } + + protected byte[] readPlane(int w, int h) { + // readData a single color plane + byte[] b = null; + int size = w * h; + if (hasLayers) { + // get RLE compression info for channel + rleEncoded = readShort() == 1; + if (rleEncoded) { + // list of encoded line lengths + readLineLengths(h); + } + } + + if (rleEncoded) { + b = readPlaneCompressed(w, h); + } else { + b = new byte[size]; + readBytes(b, size); + } + + return b; + + } + + protected byte[] readPlaneCompressed(int w, int h) { + byte[] b = new byte[w * h]; + byte[] s = new byte[w * 2]; + int pos = 0; + for (int i = 0; i < h; i++) { + if (lineIndex >= lineLengths.length) { + status = STATUS_FORMAT_ERROR; + return null; + } + int len = lineLengths[lineIndex++]; + readBytes(s, len); + decodeRLE(s, 0, len, b, pos); + pos += w; + } + return b; + } + + protected void decodeRLE(byte[] src, int sindex, int slen, byte[] dst, int dindex) { + try { + int max = sindex + slen; + while (sindex < max) { + byte b = src[sindex++]; + int n = (int) b; + if (n < 0) { + // dup next byte 1-n times + n = 1 - n; + b = src[sindex++]; + for (int i = 0; i < n; i++) { + dst[dindex++] = b; + } + } else { + // copy next n+1 bytes + n = n + 1; + System.arraycopy(src, sindex, dst, dindex, n); + dindex += n; + sindex += n; + } + } + } catch (Exception e) { + status = STATUS_FORMAT_ERROR; + } + } + + protected short readShort() { + // readData big-endian 16-bit integer + return (short) ((readByte() << 8) | readByte()); + } + + protected String readString(int len) { + // readData string of specified length + String s = ""; + for (int i = 0; i < len; i++) { + s = s + (char) readByte(); + } + return s; + } + + protected void skipBytes(int n) { + // skip over n input bytes + for (int i = 0; i < n; i++) { + readByte(); + } + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDResolutionInfo.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDResolutionInfo.java new file mode 100755 index 00000000..43102599 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDResolutionInfo.java @@ -0,0 +1,126 @@ +/* + * 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.psd; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.IIOException; +import java.io.IOException; + +/** + * PSDResolutionInfo + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$ + */ +class PSDResolutionInfo extends PSDImageResource { + // typedef struct _ResolutionInfo + // { + // LONG hRes; /* Fixed-point number: pixels per inch */ + // WORD hResUnit; /* 1=pixels per inch, 2=pixels per centimeter */ + // WORD WidthUnit; /* 1=in, 2=cm, 3=pt, 4=picas, 5=columns */ + // LONG vRes; /* Fixed-point number: pixels per inch */ + // WORD vResUnit; /* 1=pixels per inch, 2=pixels per centimeter */ + // WORD HeightUnit; /* 1=in, 2=cm, 3=pt, 4=picas, 5=columns */ + // } RESOLUTIONINFO; + + private float mHRes; + private short mHResUnit; + private short mWidthUnit; + private float mVRes; + private short mVResUnit; + private short mHeightUnit; + + + PSDResolutionInfo(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(ImageInputStream pInput) throws IOException { + if (mSize != 16) { + throw new IIOException("Resolution info length expected to be 16: " + mSize); + } + + mHRes = PSDUtil.fixedPointToFloat(pInput.readInt()); + mHResUnit = pInput.readShort(); + mWidthUnit = pInput.readShort(); + mVRes = PSDUtil.fixedPointToFloat(pInput.readInt()); + mVResUnit = pInput.readShort(); + mHeightUnit = pInput.readShort(); + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", hRes: ").append(mHRes); + builder.append(" "); + builder.append(resUnit(mHResUnit)); + builder.append(", width unit: "); + builder.append(dimUnit(mWidthUnit)); + builder.append(", vRes: ").append(mVRes); + builder.append(" "); + builder.append(resUnit(mVResUnit)); + builder.append(", height unit: "); + builder.append(dimUnit(mHeightUnit)); + + builder.append("]"); + + return builder.toString(); + } + + private String resUnit(final short pResUnit) { + switch (pResUnit) { + case 1: + return "pixels/inch"; + case 2: + return "pixels/cm"; + default: + return "unknown unit " + pResUnit; + } + } + + private String dimUnit(final short pUnit) { + switch (pUnit) { + case 1: + return "in"; + case 2: + return "cm"; + case 3: + return "pt"; + case 4: + return "pica"; + case 5: + return "column"; + default: + return "unknown unit " + pUnit; + } + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDThumbnail.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDThumbnail.java new file mode 100755 index 00000000..6e1ae79b --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDThumbnail.java @@ -0,0 +1,86 @@ +package com.twelvemonkeys.imageio.plugins.psd; + +import com.twelvemonkeys.imageio.util.IIOUtil; + +import javax.imageio.IIOException; +import javax.imageio.ImageIO; +import javax.imageio.stream.ImageInputStream; +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * PSDThumbnail + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDThumbnail.java,v 1.0 Jul 29, 2009 4:41:06 PM haraldk Exp$ + */ +class PSDThumbnail extends PSDImageResource { + private BufferedImage mThumbnail; + + public PSDThumbnail(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + /* + Thumbnail header, size 28 + 4 Format. 1 = kJpegRGB . Also supports kRawRGB (0). + 4 Width of thumbnail in pixels. + 4 Height of thumbnail in pixels. + 4 Widthbytes: Padded row bytes = (width * bits per pixel + 31) / 32 * 4. + 4 Total size = widthbytes * height * planes + 4 Size after compression. Used for consistency check. + 2 Bits per pixel. = 24 + 2 Number of planes. = 1 + */ + @Override + protected void readData(final ImageInputStream pInput) throws IOException { + // TODO: Support for RAW RGB (format == 0) + int format = pInput.readInt(); + switch (format) { + case 0: + throw new IIOException("RAW RGB format thumbnail not supported yet"); + case 1: + break; + default: + throw new IIOException(String.format("Unsupported thumbnail format (%s) in PSD document", format)); + } + + // This data isn't really useful, unless we're dealing with raw bytes + int width = pInput.readInt(); + int height = pInput.readInt(); + int widthBytes = pInput.readInt(); + int totalSize = pInput.readInt(); + + // Consistency check + int sizeCompressed = pInput.readInt(); + if (sizeCompressed != (mSize - 28)) { + throw new IIOException("Corrupt thumbnail in PSD document"); + } + + // According to the spec, only 24 bits and 1 plane is supported + int bits = pInput.readUnsignedShort(); + int planes = pInput.readUnsignedShort(); + if (bits != 24 && planes != 1) { + // TODO: Warning/Exception + } + + // TODO: Support BGR if id == RES_THUMBNAIL_PS4? Or is that already supported in the JPEG? + mThumbnail = ImageIO.read(IIOUtil.createStreamAdapter(pInput, sizeCompressed)); + } + + public BufferedImage getThumbnail() { + return mThumbnail; + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + builder.append(", ").append(mThumbnail); + + builder.append("]"); + + return builder.toString(); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java new file mode 100755 index 00000000..3865e2e3 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java @@ -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.plugins.psd; + +import com.twelvemonkeys.imageio.util.IIOUtil; +import com.twelvemonkeys.io.enc.DecoderStream; +import com.twelvemonkeys.io.enc.InflateDecoder; +import com.twelvemonkeys.io.enc.PackBitsDecoder; + +import javax.imageio.stream.ImageInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * PSDUtil + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDUtil.java,v 1.0 Apr 29, 2008 5:05:00 PM haraldk Exp$ + */ +final class PSDUtil { + // TODO: Duplicated code from IFF plugin, move to some common util? + static String intToStr(int pChunkId) { + return new String( + new byte[]{ + (byte) ((pChunkId & 0xff000000) >> 24), + (byte) ((pChunkId & 0x00ff0000) >> 16), + (byte) ((pChunkId & 0x0000ff00) >> 8), + (byte) ((pChunkId & 0x000000ff)) + } + ); + } + + // TODO: Proably also useful for PICT reader, move to some common util? + static String readPascalString(ImageInputStream pInput) throws IOException { + int length = pInput.readUnsignedByte(); +// int length = pInput.readUnsignedShort(); + byte[] bytes = new byte[length]; + pInput.readFully(bytes); + if (length % 2 == 0) { + pInput.readByte(); // Pad + } + return new String(bytes); + } + + static String readPascalStringByte(ImageInputStream pInput) throws IOException { + int length = pInput.readUnsignedByte(); + byte[] bytes = new byte[length]; + pInput.readFully(bytes); + return new String(bytes); + } + + static DataInputStream createPackBitsStream(final ImageInputStream pInput, int pLength) { + return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new PackBitsDecoder())); + } + + static DataInputStream createZipStream(final ImageInputStream pInput, int pLength) { + return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new InflateDecoder())); + } + + static DataInputStream createZipPredictorStream(final ImageInputStream pInput, int pLength) { + throw new UnsupportedOperationException("Method createZipPredictonStream not implemented"); + } + + public static float fixedPointToFloat(int pFP) { + return ((pFP & 0xffff0000) >> 16) + (pFP & 0xffff) / (float) 0xffff; + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDXMPData.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDXMPData.java new file mode 100755 index 00000000..7ee84a5e --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDXMPData.java @@ -0,0 +1,60 @@ +package com.twelvemonkeys.imageio.plugins.psd; + +import com.twelvemonkeys.lang.StringUtil; + +import javax.imageio.stream.ImageInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; + +/** + * XMP metadata. + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: XMPData.java,v 1.0 Jul 28, 2009 5:50:34 PM haraldk Exp$ + * + * @see Adobe Extensible Metadata Platform (XMP) + * @see Adobe XMP Developer Center + */ +public final class PSDXMPData extends PSDImageResource { + protected byte[] mData; + + PSDXMPData(final short pId, final ImageInputStream pInput) throws IOException { + super(pId, pInput); + } + + @Override + protected void readData(final ImageInputStream pInput) throws IOException { + mData = new byte[(int) mSize]; // TODO: Fix potential overflow, or document why that can't happen (read spec) + pInput.readFully(mData); + } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + + int length = Math.min(256, mData.length); + String data = StringUtil.decode(mData, 0, length, "UTF-8").replace('\n', ' ').replaceAll("\\s+", " "); + builder.append(", data: \"").append(data); + + if (length < mData.length) { + builder.append("..."); + } + + builder.append("\"]"); + + return builder.toString(); + } + + /** + * Returns a character stream containing the XMP metadata (XML). + * + * @return the XMP metadata. + */ + public Reader getData() { + return new InputStreamReader(new ByteArrayInputStream(mData), Charset.forName("UTF-8")); + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/YCbCrColorSpace.java b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/YCbCrColorSpace.java new file mode 100755 index 00000000..d0d020f1 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/YCbCrColorSpace.java @@ -0,0 +1,122 @@ +/* + * 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.psd; + +import java.awt.color.ColorSpace; + +/** + * YCbCrColorSpace + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: YCbCrColorSpace.java,v 1.0 Jun 28, 2008 3:30:50 PM haraldk Exp$ + */ +// TODO: Move to com.twlevemonkeys.image? +// TODO: Read an ICC YCbCr profile from classpath resource? Is there such a thing? +final class YCbCrColorSpace extends ColorSpace { + + static final ColorSpace INSTANCE = new CMYKColorSpace(); + final ColorSpace sRGB = getInstance(CS_sRGB); + + YCbCrColorSpace() { + super(ColorSpace.TYPE_YCbCr, 3); + } + + public static ColorSpace getInstance() { + return INSTANCE; + } + + // http://www.w3.org/Graphics/JPEG/jfif.txt + /* + Conversion to and from RGB + + Y, Cb, and Cr are converted from R, G, and B as defined in CCIR Recommendation 601 + but are normalized so as to occupy the full 256 levels of a 8-bit binary encoding. More + precisely: + + Y = 256 * E'y + Cb = 256 * [ E'Cb ] + 128 + Cr = 256 * [ E'Cr ] + 128 + + where the E'y, E'Cb and E'Cb are defined as in CCIR 601. Since values of E'y have a + range of 0 to 1.0 and those for E'Cb and E'Cr have a range of -0.5 to +0.5, Y, Cb, and Cr + must be clamped to 255 when they are maximum value. + + RGB to YCbCr Conversion + + YCbCr (256 levels) can be computed directly from 8-bit RGB as follows: + + Y = 0.299 R + 0.587 G + 0.114 B + Cb = - 0.1687 R - 0.3313 G + 0.5 B + 128 + Cr = 0.5 R - 0.4187 G - 0.0813 B + 128 + + NOTE - Not all image file formats store image samples in the order R0, G0, + B0, ... Rn, Gn, Bn. Be sure to verify the sample order before converting an + RGB file to JFIF. + + YCbCr to RGB Conversion + + RGB can be computed directly from YCbCr (256 levels) as follows: + + R = Y + 1.402 (Cr-128) + G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128) + B = Y + 1.772 (Cb-128) + */ + public float[] toRGB(float[] colorvalue) { +// R = Y + 1.402 (Cr-128) +// G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128) +// B = Y + 1.772 (Cb-128) + return new float[] { + colorvalue[0] + 1.402f * (colorvalue[2] - .5f), + colorvalue[0] - 0.34414f * (colorvalue[1] - .5f) - 0.71414f * (colorvalue[2] - .5f), + colorvalue[0] + 1.772f * (colorvalue[1] - .5f), + }; + // TODO: Convert via CIEXYZ space using sRGB space, as suggested in docs + // return sRGB.fromCIEXYZ(toCIEXYZ(colorvalue)); + } + + public float[] fromRGB(float[] rgbvalue) { +// Y = 0.299 R + 0.587 G + 0.114 B +// Cb = - 0.1687 R - 0.3313 G + 0.5 B + 128 +// Cr = 0.5 R - 0.4187 G - 0.0813 B + 128 + return new float[] { + 0.299f * rgbvalue[0] + 0.587f * rgbvalue[1] + 0.114f * rgbvalue[2], + -0.1687f * rgbvalue[0] - 0.3313f * rgbvalue[1] + 0.5f * rgbvalue[2] + .5f, + 0.5f * rgbvalue[0] - 0.4187f * rgbvalue[1] - 0.0813f * rgbvalue[2] + .5f + }; + } + + public float[] toCIEXYZ(float[] colorvalue) { + throw new UnsupportedOperationException("Method toCIEXYZ not implemented"); // TODO: Implement + } + + public float[] fromCIEXYZ(float[] colorvalue) { + throw new UnsupportedOperationException("Method fromCIEXYZ not implemented"); // TODO: Implement + } +} diff --git a/twelvemonkeys-imageio/psd/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/twelvemonkeys-imageio/psd/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi new file mode 100755 index 00000000..21a911af --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi @@ -0,0 +1 @@ +com.twelvemonkeys.imageio.plugins.psd.PSDImageReaderSpi \ No newline at end of file diff --git a/twelvemonkeys-imageio/psd/src/test/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderTestCase.java b/twelvemonkeys-imageio/psd/src/test/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderTestCase.java new file mode 100755 index 00000000..498eb398 --- /dev/null +++ b/twelvemonkeys-imageio/psd/src/test/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderTestCase.java @@ -0,0 +1,68 @@ +package com.twelvemonkeys.imageio.plugins.psd; + +import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase; + +import javax.imageio.spi.ImageReaderSpi; +import java.awt.*; +import java.util.Arrays; +import java.util.List; + +/** + * PSDImageReaderTestCase + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: PSDImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ + */ +public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase { + + static ImageReaderSpi sProvider = new PSDImageReaderSpi(); + + protected List getTestData() { + return Arrays.asList( + // 5 channel, RGB + new TestData(getClassLoaderResource("/psd/photoshopping.psd"), new Dimension(300, 225)), + // 1 channel, gray, 8 bit samples + new TestData(getClassLoaderResource("/psd/buttons.psd"), new Dimension(20, 20)), + // 5 channel, CMYK + new TestData(getClassLoaderResource("/psd/escenic-liquid-logo.psd"), new Dimension(595, 420)), + // 3 channel RGB, no composite layer + new TestData(getClassLoaderResource("/psd/jugware-icon.psd"), new Dimension(128, 128)), + // 3 channel RGB, old data, no layer info/mask + new TestData(getClassLoaderResource("/psd/MARBLES.PSD"), new Dimension(1419, 1001)), + // 1 channel, indexed color + new TestData(getClassLoaderResource("/psd/coral_fish.psd"), new Dimension(800, 800)) + // 1 channel, bitmap, 1 bit samples +// new TestData(getClassLoaderResource("/psd/test_bitmap.psd"), new Dimension(800, 600)) + // 1 channel, gray, 16 bit samples +// new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(800, 600)) + // TODO: Need uncompressed PSD + // TODO: Need more recent ZIP compressed PSD files from CS2/CS3+ + ); + } + + protected ImageReaderSpi createProvider() { + return sProvider; + } + + @Override + protected PSDImageReader createReader() { + return new PSDImageReader(sProvider); + } + + protected Class getReaderClass() { + return PSDImageReader.class; + } + + protected List getFormatNames() { + return Arrays.asList("psd"); + } + + protected List getSuffixes() { + return Arrays.asList("psd"); + } + + protected List getMIMETypes() { + return Arrays.asList("image/x-psd"); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/AlphaChannelSample_720x480.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/AlphaChannelSample_720x480.psd new file mode 100755 index 00000000..5b70ee40 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/AlphaChannelSample_720x480.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/MARBLES.PSD b/twelvemonkeys-imageio/psd/src/test/resources/psd/MARBLES.PSD new file mode 100755 index 00000000..f15802f8 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/MARBLES.PSD differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/buttons.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/buttons.psd new file mode 100755 index 00000000..a56f6f21 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/buttons.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/circles.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/circles.psd new file mode 100755 index 00000000..f4d68338 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/circles.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/cmyk_16bits.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/cmyk_16bits.psd new file mode 100755 index 00000000..ec6fec51 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/cmyk_16bits.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/cmyk_8bits.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/cmyk_8bits.psd new file mode 100755 index 00000000..5a822f76 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/cmyk_8bits.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/coral_fish.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/coral_fish.psd new file mode 100755 index 00000000..2bbb5128 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/coral_fish.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/escenic-liquid-logo.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/escenic-liquid-logo.psd new file mode 100755 index 00000000..99b0cdd3 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/escenic-liquid-logo.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/jugware-icon.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/jugware-icon.psd new file mode 100755 index 00000000..ebf9ab9b Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/jugware-icon.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/photoshopping.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/photoshopping.psd new file mode 100755 index 00000000..41c880bf Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/photoshopping.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/test_bitmap.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_bitmap.psd new file mode 100755 index 00000000..73247167 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_bitmap.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/test_gray.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_gray.psd new file mode 100755 index 00000000..9f828b57 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_gray.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/test_gray16.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_gray16.psd new file mode 100755 index 00000000..e3b139a1 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_gray16.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/test_indexed.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_indexed.psd new file mode 100755 index 00000000..ad8d57a5 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_indexed.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/test_original.psb b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_original.psb new file mode 100755 index 00000000..a5bfb5f9 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_original.psb differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/test_original.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_original.psd new file mode 100755 index 00000000..0f553e47 Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/test_original.psd differ diff --git a/twelvemonkeys-imageio/psd/src/test/resources/psd/transp.psd b/twelvemonkeys-imageio/psd/src/test/resources/psd/transp.psd new file mode 100755 index 00000000..d3ddb5cd Binary files /dev/null and b/twelvemonkeys-imageio/psd/src/test/resources/psd/transp.psd differ diff --git a/twelvemonkeys-imageio/psd/todo.txt b/twelvemonkeys-imageio/psd/todo.txt new file mode 100755 index 00000000..efe9ceee --- /dev/null +++ b/twelvemonkeys-imageio/psd/todo.txt @@ -0,0 +1,5 @@ +Implement source subsampling and region of interest +Separate package for the resources (seems to be a lot)? +Possibility to read only some resources? readResources(int[] resourceKeys)? + - Probably faster when we only need the color space +PSDImageWriter \ No newline at end of file diff --git a/twelvemonkeys-imageio/thumbsdb/license.txt b/twelvemonkeys-imageio/thumbsdb/license.txt new file mode 100755 index 00000000..2d8ee79c --- /dev/null +++ b/twelvemonkeys-imageio/thumbsdb/license.txt @@ -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. \ No newline at end of file diff --git a/twelvemonkeys-imageio/thumbsdb/pom.xml b/twelvemonkeys-imageio/thumbsdb/pom.xml new file mode 100755 index 00000000..e8f4513f --- /dev/null +++ b/twelvemonkeys-imageio/thumbsdb/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + com.twelvemonkeys.imageio + twelvemonkeys-imageio-thumbsdb + 2.1 + TwelveMonkeys ImageIO Thumbs.db plugin + + ImageIO plugin for Windows Thumbs DB (Thumbs.db) format. + + + + twelvemonkeys-imageio + com.twelvemonkeys + 2.1 + + + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + + + com.twelvemonkeys.imageio + twelvemonkeys-imageio-core + tests + + + \ No newline at end of file diff --git a/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/Catalog.java b/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/Catalog.java new file mode 100755 index 00000000..51a6f288 --- /dev/null +++ b/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/Catalog.java @@ -0,0 +1,259 @@ +/* + * 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.thumbsdb; + +import com.twelvemonkeys.io.LittleEndianDataInputStream; +import com.twelvemonkeys.io.ole2.CompoundDocument; +import com.twelvemonkeys.lang.StringUtil; + +import java.io.InputStream; +import java.io.IOException; +import java.io.DataInput; +import java.util.Iterator; +import java.util.Date; + +/** + * Represents a {@code Catalog} structure, typically found in a {@link com.twelvemonkeys.io.ole2.CompoundDocument}. + * + * @see PeteDavis.NET + * + * @author Harald Kuhr + * @author last modified by $Author: haku$ + * @version $Id: Catalog.java,v 1.0 01.feb.2007 17:19:59 haku Exp$ + */ +// TODO: Consider moving this one to io.ole2 +public final class Catalog implements Iterable { + + private final CatalogHeader mHeader; + private final CatalogItem[] mItems; + + Catalog(final CatalogHeader pHeader, final CatalogItem[] pItems) { + mHeader = pHeader; + mItems = pItems; + } + + /** + * Reads the {@code Catalog} entry from the given input stream. + * + * @param pInput the input stream + * @return a new {@code Catalog} + * + * @throws java.io.IOException if an I/O exception occurs during read + */ + public static Catalog read(final InputStream pInput) throws IOException { + DataInput dataInput = new LittleEndianDataInputStream(pInput); + return read(dataInput); + } + + /** + * Reads the {@code Catalog} entry from the given input stream. + *

+ * The data is assumed to be in little endian byte order. + * + * @param pDataInput the input stream + * @return a new {@code Catalog} + * + * @throws java.io.IOException if an I/O exception occurs during read + */ + public static Catalog read(final DataInput pDataInput) throws IOException { + CatalogHeader header = CatalogHeader.read(pDataInput); + + CatalogItem[] items = new CatalogItem[header.getThumbnailCount()]; + for (int i = 0; i < header.getThumbnailCount(); i++) { + CatalogItem item = CatalogItem.read(pDataInput); + //System.out.println("item: " + item); + items[item.getItemId() - 1] = item; + } + + return new Catalog(header, items); + } + + public final int getThumbnailCount() { + return mHeader.mThumbCount; + } + + public final int getMaxThumbnailWidth() { + return mHeader.mThumbWidth; + } + + public final int getMaxThumbnailHeight() { + return mHeader.mThumbHeight; + } + + final CatalogItem getItem(final int pIndex) { + return mItems[pIndex]; + } + + final CatalogItem getItem(final String pName) { + return mItems[getIndex(pName)]; + } + + final int getItemId(final int pIndex) { + return mItems[pIndex].getItemId(); + } + + public final int getIndex(final String pName) { + for (int i = 0; i < mItems.length; i++) { + CatalogItem item = mItems[i]; + + if (item.getName().equals(pName)) { + return i; + } + } + + return -1; + } + + public final String getStreamName(final int pIndex) { + return StringUtil.reverse(String.valueOf(getItemId(pIndex))); + } + + public final String getName(String pStreamName) { + return getName(Integer.parseInt(StringUtil.reverse(pStreamName))); + } + + final String getName(int pItemId) { + return mItems[pItemId - 1].getName(); + } + + @Override + public String toString() { + return String.format("%s[%s]", getClass().getSimpleName(), mHeader); + } + + public Iterator iterator() { + return new Iterator() { + int mCurrentIdx; + + public boolean hasNext() { + return mCurrentIdx < mItems.length; + } + + public CatalogItem next() { + return mItems[mCurrentIdx++]; + } + + public void remove() { + throw new UnsupportedOperationException("Remove not supported"); + } + }; + } + + private static class CatalogHeader { + short mReserved1; + short mReserved2; + int mThumbCount; + int mThumbWidth; + int mThumbHeight; + + CatalogHeader() { + } + + public static CatalogHeader read(final DataInput pDataInput) throws IOException { + CatalogHeader header = new CatalogHeader(); + + header.mReserved1 = pDataInput.readShort(); + header.mReserved2 = pDataInput.readShort(); + header.mThumbCount = pDataInput.readInt(); + header.mThumbWidth = pDataInput.readInt(); + header.mThumbHeight = pDataInput.readInt(); + + return header; + } + + public int getThumbnailCount() { + return mThumbCount; + } + + public int getThumbHeight() { + return mThumbHeight; + } + + public int getThumbWidth() { + return mThumbWidth; + } + + @Override + public String toString() { + return String.format( + "%s: %s %s thumbs: %d maxWidth: %d maxHeight: %d", + getClass().getSimpleName(), mReserved1, mReserved2, mThumbCount, mThumbWidth, mThumbHeight + ); + } + } + + public static final class CatalogItem { + int mReserved1; + int mItemId; // Reversed stream name + String mFilename; + short mReserved2; + private long mLastModified; + + private static CatalogItem read(final DataInput pDataInput) throws IOException { + CatalogItem item = new CatalogItem(); + item.mReserved1 = pDataInput.readInt(); + item.mItemId = pDataInput.readInt(); + + item.mLastModified = CompoundDocument.toJavaTimeInMillis(pDataInput.readLong()); + + char[] chars = new char[256]; + char ch; + int len = 0; + while ((ch = pDataInput.readChar()) != 0) { + chars[len++] = ch; + } + + String name = new String(chars, 0, len); + item.mFilename = StringUtil.getLastElement(name, "\\"); + + item.mReserved2 = pDataInput.readShort(); + return item; + } + + public String getName() { + return mFilename; + } + + public int getItemId() { + return mItemId; + } + + public long lastModified() { + return mLastModified; + } + + @Override + public String toString() { + return String.format( + "%s: %d itemId: %d lastModified: %s fileName: %s %s", + getClass().getSimpleName(), mReserved1, mItemId, new Date(mLastModified), mFilename, mReserved2 + ); + } + } +} diff --git a/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReader.java b/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReader.java new file mode 100755 index 00000000..33929f50 --- /dev/null +++ b/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReader.java @@ -0,0 +1,411 @@ +/* + * 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.thumbsdb; + +import com.twelvemonkeys.imageio.ImageReaderBase; +import com.twelvemonkeys.imageio.util.ProgressListenerBase; +import com.twelvemonkeys.io.FileUtil; +import com.twelvemonkeys.io.ole2.CompoundDocument; +import com.twelvemonkeys.io.ole2.Entry; +import com.twelvemonkeys.lang.StringUtil; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.MemoryCacheImageInputStream; +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.Iterator; +import java.util.SortedSet; + +/** + * ThumbsDBImageReader + * + * @author Harald Kuhr + * @author last modified by $Author: haku$ + * @version $Id: ThumbsDBImageReader.java,v 1.0 22.jan.2007 18:49:38 haku Exp$ + * @see com.twelvemonkeys.io.ole2.CompoundDocument + * @see getImageTypes(int pIndex) throws IOException { + init(pIndex); + return mReader.getImageTypes(0); + } + + public boolean isPresent(final String pFileName) { + try { + init(); + } + catch (IOException e) { + resetMembers(); + return false; + } + + // TODO: Rethink this... + // Seems to be up to Windows and the installed programs what formats + // are supported... + // Some thumbs are just icons, and it might be better to use ImageIO to create thumbs for these... :-/ + // At least this seems fine for now + String extension = FileUtil.getExtension(pFileName); + if (StringUtil.isEmpty(extension)) { + return false; + } + extension = extension.toLowerCase(); + + return !"psd".equals(extension) && !"svg".equals(extension) && mCatalog != null && mCatalog.getIndex(pFileName) != -1; + } + + /// Test code below + + public static void main(String[] pArgs) throws IOException { + ThumbsDBImageReader reader = new ThumbsDBImageReader(); + reader.setInput(ImageIO.createImageInputStream(new File(pArgs[0]))); +// reader.setLoadEagerly(true); + + if (pArgs.length > 1) { + long start = System.currentTimeMillis(); + reader.init(); + for (Catalog.CatalogItem item : reader.mCatalog) { + reader.read(item.getName(), null); + } + long end = System.currentTimeMillis(); + System.out.println("Time: " + (end - start) + " ms"); + } + else { + JFrame frame = createWindow(pArgs[0]); + JPanel panel = new JPanel(); + panel.setBackground(Color.WHITE); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + + long start = System.currentTimeMillis(); + reader.init(); + for (Catalog.CatalogItem item : reader.mCatalog) { + addImage(panel, reader, reader.mCatalog.getIndex(item.getName()), item.getName()); + } + long end = System.currentTimeMillis(); + System.out.println("Time: " + (end - start) + " ms"); + + frame.getContentPane().add(new JScrollPane(panel)); + frame.pack(); + frame.setVisible(true); + } + } + + + 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(Container pParent, ImageReader pReader, int pImageNo, String pName) throws IOException { + final JLabel label = new JLabel(); + final BufferedImage image = pReader.read(pImageNo); + label.setIcon(new Icon() { + private static final int SIZE = 110; + + public void paintIcon(Component c, Graphics g, int x, int y) { + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setColor(Color.DARK_GRAY); + g.fillRoundRect(x, y, SIZE, SIZE, 10, 10); + g.drawImage(image, (SIZE - image.getWidth()) / 2 + x, (SIZE - image.getHeight()) / 2 + y, null); + } + + public int getIconWidth() { + return SIZE; + } + + public int getIconHeight() { + return SIZE; + } + }); + label.setText("" + image.getWidth() + "x" + image.getHeight() + ": " + pName); + label.setToolTipText(image.toString()); + pParent.add(label); + } + +} diff --git a/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReaderSpi.java b/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReaderSpi.java new file mode 100755 index 00000000..a392d882 --- /dev/null +++ b/twelvemonkeys-imageio/thumbsdb/src/main/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReaderSpi.java @@ -0,0 +1,173 @@ +/* + * 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.thumbsdb; + +import com.twelvemonkeys.io.ole2.CompoundDocument; + +import javax.imageio.ImageReader; +import javax.imageio.spi.IIORegistry; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.spi.ServiceRegistry; +import javax.imageio.stream.ImageInputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Locale; + +/** + * ThumbsDBImageReaderSpi + *

+ * + * @author Harald Kuhr + * @version $Id: ThumbsDBImageReaderSpi.java,v 1.0 28.feb.2006 19:21:05 haku Exp$ + */ +public class ThumbsDBImageReaderSpi extends ImageReaderSpi { + private ImageReaderSpi mJPEGProvider; + + /** + * Creates an IFFImageReaderSpi + */ + public ThumbsDBImageReaderSpi() { + super( + "TwelveMonkeys", + "2.0", + new String[]{"thumbs", "THUMBS", "Thumbs DB"}, + new String[]{"db"}, + new String[]{"image/x-thumbs-db", "application/octet-stream"}, // TODO: Check IANA et al... + ThumbsDBImageReader.class.getName(), + STANDARD_INPUT_TYPE, + null, + true, null, null, null, null, + true, null, null, null, null + ); + } + + public boolean canDecodeInput(Object source) throws IOException { + return source instanceof ImageInputStream && canDecode((ImageInputStream) source); + } + + public boolean canDecode(ImageInputStream pInput) throws IOException { + maybeInitJPEGProvider(); + // If this is a OLE 2 CompoundDocument, we could try... + // TODO: How do we know it's thumbs.db format (structure), without reading quite a lot? + return mJPEGProvider != null && CompoundDocument.canRead(pInput); + } + + public ImageReader createReaderInstance(Object extension) throws IOException { + return new ThumbsDBImageReader(this); + } + + private void maybeInitJPEGProvider() { + // NOTE: Can't do this from constructor, as ImageIO itself is not initialized yet, + // and the lookup below will produce a NPE.. + + // TODO: A better approach... + // - Could have a list with known working JPEG decoders? + // - System property? + // - Class path lookup of properties file with reader? + // This way we could deregister immediately + + if (mJPEGProvider == null) { + ImageReaderSpi provider = null; + try { + Iterator providers = getJPEGProviders(); + + while (providers.hasNext()) { + provider = providers.next(); + + // Prefer the one we know + if ("Sun Microsystems, Inc.".equals(provider.getVendorName())) { + break; + } + } + } + catch (Exception ignore) { + // It's pretty safe to assume there's always a JPEG reader out there + // In any case, we deregister the provider if there isn't one + IIORegistry.getDefaultInstance().deregisterServiceProvider(this, ImageReaderSpi.class); + } + mJPEGProvider = provider; + } + } + + private Iterator getJPEGProviders() { + return IIORegistry.getDefaultInstance().getServiceProviders( + ImageReaderSpi.class, + new ServiceRegistry.Filter() { + public boolean filter(Object provider) { + if (provider instanceof ImageReaderSpi) { + ImageReaderSpi spi = (ImageReaderSpi) provider; + for (String format : spi.getFormatNames()) { + if ("JPEG".equals(format)) { + return true; + } + } + } + return false; + } + }, true + ); + } + + /** + * Returns a new {@code ImageReader} that can read JPEG images. + * + * @return a new {@code ImageReader} that can read JPEG images. + * @throws IllegalStateException if no JPEG provider was found + * @throws Error if the reader can't be instantiated + */ + ImageReader createJPEGReader() { + maybeInitJPEGProvider(); + if (mJPEGProvider == null) { + throw new IllegalStateException("No suitable JPEG reader provider found"); + } + + try { + return mJPEGProvider.createReaderInstance(); + } + catch (IOException e) { + // NOTE: The default Sun version never throws IOException here + throw new Error("Could not create JPEG reader: " + e.getMessage(), e); + } + } + + public String getDescription(Locale locale) { + return "Microsoft Windows Thumbs DB (Thumbs.db) image reader"; + } + +// @Override +// public void onRegistration(ServiceRegistry registry, Class category) { +// System.out.println("ThumbsDBImageReaderSpi.onRegistration"); +// maybeInitJPEGProvider(); +// if (mJPEGProvider == null) { +// System.out.println("Deregistering"); +// registry.deregisterServiceProvider(this, ImageReaderSpi.class); +// } +// } + +} diff --git a/twelvemonkeys-imageio/thumbsdb/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/twelvemonkeys-imageio/thumbsdb/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi new file mode 100755 index 00000000..374d7633 --- /dev/null +++ b/twelvemonkeys-imageio/thumbsdb/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi @@ -0,0 +1 @@ +com.twelvemonkeys.imageio.plugins.thumbsdb.ThumbsDBImageReaderSpi diff --git a/twelvemonkeys-imageio/thumbsdb/src/test/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReaderTestCase.java b/twelvemonkeys-imageio/thumbsdb/src/test/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReaderTestCase.java new file mode 100755 index 00000000..63aacaf2 --- /dev/null +++ b/twelvemonkeys-imageio/thumbsdb/src/test/java/com/twelvemonkeys/imageio/plugins/thumbsdb/ThumbsDBImageReaderTestCase.java @@ -0,0 +1,114 @@ +/* + * 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.thumbsdb; + +import com.twelvemonkeys.imageio.stream.BufferedImageInputStream; +import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase; +import com.twelvemonkeys.io.ole2.CompoundDocument; +import com.twelvemonkeys.io.ole2.Entry; + +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.MemoryCacheImageInputStream; +import java.awt.*; +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.List; + +/** + * ICOImageReaderTestCase + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: ICOImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ + */ +public class ThumbsDBImageReaderTestCase extends ImageReaderAbstractTestCase { + private ThumbsDBImageReaderSpi mProvider = new ThumbsDBImageReaderSpi(); + + protected List getTestData() { + return Arrays.asList( + new TestData( + getClassLoaderResource("/thumbsdb/Thumbs.db"), + new Dimension(96, 96), new Dimension(96, 96), new Dimension(16, 16), + new Dimension(96, 45), new Dimension(63, 96), new Dimension(96, 96), + new Dimension(96, 64), new Dimension(96, 96), new Dimension(96, 77) + ), + new TestData( + getClassLoaderResource("/thumbsdb/Thumbs-camera.db"), + new Dimension(96, 96), + new Dimension(64, 96), + new Dimension(96, 96), + new Dimension(96, 64), new Dimension(64, 96), + new Dimension(96, 64), new Dimension(96, 64), new Dimension(96, 64), + new Dimension(64, 96), new Dimension(64, 96), new Dimension(64, 96), + new Dimension(64, 96), new Dimension(64, 96), new Dimension(64, 96), + new Dimension(96, 64), + new Dimension(64, 96), + new Dimension(96, 64), new Dimension(96, 64), new Dimension(96, 64), + new Dimension(64, 96), new Dimension(64, 96), + new Dimension(64, 96), new Dimension(64, 96), new Dimension(64, 96) + ) + ); + } + + protected ImageReaderSpi createProvider() { + return mProvider; + } + + @Override + protected ThumbsDBImageReader createReader() { + return new ThumbsDBImageReader(mProvider); + } + + protected Class getReaderClass() { + return ThumbsDBImageReader.class; + } + + protected List getFormatNames() { + return Arrays.asList("thumbs"); + } + + protected List getSuffixes() { + return Arrays.asList("db"); + } + + protected List getMIMETypes() { + return Arrays.asList("image/x-thumbs-db"); + } + + public void testArrayIndexOutOfBoundsBufferedReadBug() throws IOException { + ImageInputStream input = new BufferedImageInputStream(new MemoryCacheImageInputStream(getClass().getResourceAsStream("/thumbsdb/Thumbs-camera.db"))); + input.setByteOrder(ByteOrder.LITTLE_ENDIAN); + Entry root = new CompoundDocument(input).getRootEntry(); + + Entry child = root.getChildEntry("Catalog"); + child.getInputStream(); + } +} \ No newline at end of file diff --git a/twelvemonkeys-imageio/thumbsdb/src/test/resources/thumbsdb/Thumbs-camera.db b/twelvemonkeys-imageio/thumbsdb/src/test/resources/thumbsdb/Thumbs-camera.db new file mode 100755 index 00000000..1adedbb8 Binary files /dev/null and b/twelvemonkeys-imageio/thumbsdb/src/test/resources/thumbsdb/Thumbs-camera.db differ diff --git a/twelvemonkeys-imageio/thumbsdb/todo.txt b/twelvemonkeys-imageio/thumbsdb/todo.txt new file mode 100755 index 00000000..ca61a0e0 --- /dev/null +++ b/twelvemonkeys-imageio/thumbsdb/todo.txt @@ -0,0 +1,14 @@ +- Listeners + +- Expose one of the methods below: + - String getName(int pIndex) + - String[] getNames() + - Catalog getCatalog() + +- Make it multi-threaded? Or at least support multi-threaded reading + +- There seems to be different versions of the Thumbs.db: + - http://vinetto.sourceforge.net/docs.html + - We probably want to support all of these + +DONE: diff --git a/twelvemonkeys-imageio/todo.txt b/twelvemonkeys-imageio/todo.txt new file mode 100755 index 00000000..8fa3c43f --- /dev/null +++ b/twelvemonkeys-imageio/todo.txt @@ -0,0 +1,11 @@ +Consider creating a raw ImageReader (or util class?) that can read raw bitmaps: + - Interleaved (A)RGB (as in BMP, PICT, IFF PBM etc) -> A1R1G1B1, A2R2G2B2, ..., AnRnGnNn + - Channeled (A)RGB (as in Photoshop) -> A1A2...An, R1R2...Rn, G1G2...Gn, B1B2...Bn + - Planar RGB (as in IFF ILBM) -> .... +Formats that internally have these structures could delegate to an instance of this class. +Could also be interesting to allow for raw reading using a RawImageReader. + - Would need to specify width, height + - Pixel layout + - Channel order + - Compression? RLE/PackBits/LZW/ZIP? + - IndexColorModel?