All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twelvemonkeys.imageio.plugins.pict.PICTImageReader Maven / Gradle / Ivy

The newest version!
/*
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.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.io.enc.Decoder;
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.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 Some MAJOR clean up * @todo As we now have Graphics2D with more options, support more of the format? */ public final class PICTImageReader extends ImageReaderBase { final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.pict.debug")); private QuickDrawContext context; private Rectangle frame; // TODO: Do we need this? private int version; // Variables for storing draw status // TODO: Get rid of these, or move to context private Point penPosition = new Point(0, 0); private Rectangle lastRectangle = new Rectangle(0, 0); // Ratio between the screen resolution and the image resolution private double screenImageXRatio; private double screenImageYRatio; // List of images created during image import private List images = new ArrayList(); private long imageStartStreamPos; protected int picSize; public PICTImageReader() { this(null); } protected PICTImageReader(final ImageReaderSpi pProvider) { super(pProvider); } protected void resetMembers() { context = null; frame = null; images.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 (frame == null) { // Read in header information readPICTHeader(imageInput); if (DEBUG) { System.out.println("Done reading PICT header!"); } } return frame; } /** * 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 PICTImageReaderSpi.skipNullHeader(pStream); readPICTHeader0(pStream); } } private void readPICTHeader0(final ImageInputStream pStream) throws IOException { // Get size picSize = pStream.readUnsignedShort(); if (DEBUG) { System.out.println("picSize: " + picSize); } // Get frame at 72 dpi // NOTE: These are not pixel sizes! // Need to be multiplied with hRes/screenResolution and vRes/screenResolution int y = pStream.readUnsignedShort(); int x = pStream.readUnsignedShort(); int h = pStream.readUnsignedShort(); int w = pStream.readUnsignedShort(); frame = new Rectangle(x, y, w - x, h - y); if (frame.width < 0 || frame.height < 0) { throw new IIOException("Error in PICT header: Invalid frame " + frame); } if (DEBUG) { System.out.println("frame: " + frame); } // Set default display ratios. 72 dpi is the standard Macintosh resolution. screenImageXRatio = 1.0; screenImageYRatio = 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) { this.version = 1; } else if (version == PICT.OP_VERSION && pStream.readShort() == PICT.OP_VERSION_2) { this.version = 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); screenImageXRatio = (w - x) / (w2 - x2); screenImageYRatio = (h - y) / (h2 - y2); if (screenImageXRatio < 0 || screenImageYRatio < 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(); screenImageXRatio = (w - x) / (double) (w2 - x2); screenImageYRatio = (h - y) / (double) (h2 - y2); if (screenImageXRatio < 0 || screenImageYRatio < 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: " + screenImageXRatio); System.out.println("screenImageYRatio: " + screenImageYRatio); } } 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: " + this.version + (isExtendedV2 ? " extended" : "")); } imageStartStreamPos = pStream.getStreamPosition(); // Won't need header data again (NOTE: We'll only get here if no exception is thrown) pStream.flushBefore(imageStartStreamPos); } /** * 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 { context = new QuickDrawContext(pGraphics); readPICTopcodes(imageInput); 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(imageStartStreamPos); int opCode, dh, dv, dataLength; byte[] colorBuffer = new byte[3 * PICT.COLOR_COMP_SIZE]; Pattern fill = QuickDraw.BLACK; Pattern bg; Pattern pen; 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 (version == 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 context.setBackgroundPattern(PICTUtil.readPattern(pStream)); if (DEBUG) { System.out.println("bkPat"); } break; case PICT.OP_TX_FONT: // Get the data byte[] fontData = new byte[2]; pStream.readFully(fontData, 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: " + Arrays.toString(fontData)); } break; case PICT.OP_TX_FACE: // Get the data int txFace = pStream.readUnsignedByte(); context.setTextFace(txFace); if (DEBUG) { System.out.println("txFace: " + txFace); } break; case PICT.OP_TX_MODE: // 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()); if (DEBUG) { System.out.println("pnsize: " + pnsize); } context.setPenSize(pnsize); break; case PICT.OP_PN_MODE: // Get the data int mode = pStream.readUnsignedShort(); if (DEBUG) { System.out.println("pnMode: " + mode); } context.setPenMode(mode); break; case PICT.OP_PN_PAT: context.setPenPattern(PICTUtil.readPattern(pStream, context.getForeground(), context.getBackground())); 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 (DEBUG) { System.out.println("Origin: " + origin); } break; case PICT.OP_TX_SIZE:// OK // Get the text size int tx_size = getYPtCoord(pStream.readUnsignedShort()); context.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); context.setBackgroundPattern(bg); if (DEBUG) { System.out.println("BkPixPat"); } break; case 0x0013: // PnPixPat pen = PICTUtil.readColorPattern(pStream); context.setPenPattern(pen); if (DEBUG) { System.out.println("PnPixPat"); } break; case 0x0014: // FillPixPat fill = PICTUtil.readColorPattern(pStream); context.setFillPattern(fill); if (DEBUG) { System.out.println("FillPixPat"); } 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); Color foreground = new Color((colorBuffer[0] & 0xFF), (colorBuffer[2] & 0xFF), (colorBuffer[4] & 0xFF)); if (DEBUG) { System.out.println("rgbFgColor: " + foreground); } context.setForeground(foreground); break; case PICT.OP_RGB_BK_COL:// OK // Get the color pStream.readFully(colorBuffer, 0, colorBuffer.length); // The color might be 16 bit per component.. Color background = new Color(colorBuffer[0] & 0xFF, colorBuffer[2] & 0xFF, colorBuffer[4] & 0xFF); if (DEBUG) { System.out.println("rgbBgColor: " + background); } context.setBackground(background); break; case PICT.OP_HILITE_MODE: // Change color to hilite color context.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()); penPosition.setLocation(x, y); // Move pen to new position, draw line context.moveTo(origin); context.lineTo(penPosition); if (DEBUG) { System.out.println("line from: " + origin + " to: " + penPosition); } break; case PICT.OP_LINE_FROM:// OK, not tested // Get the point y = getYPtCoord(pStream.readUnsignedShort()); x = getXPtCoord(pStream.readUnsignedShort()); // Draw line context.line(x, y); if (DEBUG) { System.out.println("lineFrom to: " + penPosition); } 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); x = getXPtCoord(pStream.readByte()); y = getYPtCoord(pStream.readByte()); dh_dv = new Point(x, y); // Move pen to new position, draw line if we have a graphics context.moveTo(origin); penPosition.setLocation(origin.x + dh_dv.x, origin.y + dh_dv.y); context.lineTo(penPosition); if (DEBUG) { System.out.println("Short line origin: " + origin + ", dh,dv: " + dh_dv); } break; case PICT.OP_SHORT_LINE_FROM:// OK // Get dh, dv x = getXPtCoord(pStream.readByte()); y = getYPtCoord(pStream.readByte()); // Draw line context.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); penPosition = origin; context.moveTo(penPosition); text = PICTUtil.readPascalString(pStream); // TODO //if (mGraphics != null) { // mGraphics.drawString(text, penPosition.x, penPosition.y); //} context.drawString(text); if (DEBUG) { System.out.println("longText origin: " + penPosition + ", text:" + text); } break; case PICT.OP_DH_TEXT:// OK, not tested // Get dh dh = getXPtCoord(pStream.readByte()); penPosition.translate(dh, 0); context.moveTo(penPosition); text = PICTUtil.readPascalString(pStream); context.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()); penPosition.translate(0, dv); context.moveTo(penPosition); text = PICTUtil.readPascalString(pStream); context.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()); penPosition.translate(x, y); context.moveTo(penPosition); text = PICTUtil.readPascalString(pStream); context.drawString(text); if (DEBUG) { System.out.println("DHDVText penPosition: " + penPosition + ", text:" + text); } break; case PICT.OP_FONT_NAME:// OK, not tested // Get data length /*data_len = */ pStream.readShort(); // Get old font ID, ignored pStream.readUnsignedShort(); // Get font name and set the new font if we have one String fontName = PICTUtil.readPascalString(pStream); context.setTextFont(fontName); if (DEBUG) { System.out.println("fontName: \"" + fontName +"\""); } break; case PICT.OP_LINE_JUSTIFY:// TODO // Get data byte[] lineJustifyData = new byte[10]; pStream.readFully(lineJustifyData, 0, lineJustifyData.length); if (DEBUG) { System.out.println("opLineJustify"); } break; case PICT.OP_GLYPH_STATE:// TODO: NOT SUPPORTED IN AWT GRAPHICS YET? // Get data byte[] glyphState = new byte[6]; pStream.readFully(glyphState, 0, glyphState.length); if (DEBUG) { System.out.println("glyphState: " + Arrays.toString(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, lastRectangle); 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: context.frameRect(lastRectangle); break; case PICT.OP_PAINT_RECT: case PICT.OP_PAINT_SAME_RECT: context.paintRect(lastRectangle); break; case PICT.OP_ERASE_RECT: case PICT.OP_ERASE_SAME_RECT: context.eraseRect(lastRectangle); break; case PICT.OP_INVERT_RECT: case PICT.OP_INVERT_SAME_RECT: context.invertRect(lastRectangle); break; case PICT.OP_FILL_RECT: case PICT.OP_FILL_SAME_RECT: context.fillRect(lastRectangle, fill); break; } // Do verbose mode output if (DEBUG) { switch (opCode) { case PICT.OP_FRAME_RECT: System.out.println("frameRect: " + lastRectangle); break; case PICT.OP_PAINT_RECT: System.out.println("paintRect: " + lastRectangle); break; case PICT.OP_ERASE_RECT: System.out.println("eraseRect: " + lastRectangle); break; case PICT.OP_INVERT_RECT: System.out.println("invertRect: " + lastRectangle); break; case PICT.OP_FILL_RECT: System.out.println("fillRect: " + lastRectangle); break; case PICT.OP_FRAME_SAME_RECT: System.out.println("frameSameRect: " + lastRectangle); break; case PICT.OP_PAINT_SAME_RECT: System.out.println("paintSameRect: " + lastRectangle); break; case PICT.OP_ERASE_SAME_RECT: System.out.println("eraseSameRect: " + lastRectangle); break; case PICT.OP_INVERT_SAME_RECT: System.out.println("invertSameRect: " + lastRectangle); break; case PICT.OP_FILL_SAME_RECT: System.out.println("fillSameRect: " + lastRectangle); 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, lastRectangle); 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: context.frameRoundRect(lastRectangle, ovSize.x, ovSize.y); break; case PICT.OP_PAINT_R_RECT: case PICT.OP_PAINT_SAME_R_RECT: context.paintRoundRect(lastRectangle, ovSize.x, ovSize.y); break; case PICT.OP_ERASE_R_RECT: case PICT.OP_ERASE_SAME_R_RECT: context.eraseRoundRect(lastRectangle, ovSize.x, ovSize.y); break; case PICT.OP_INVERT_R_RECT: case PICT.OP_INVERT_SAME_R_RECT: context.invertRoundRect(lastRectangle, ovSize.x, ovSize.y); break; case PICT.OP_FILL_R_RECT: case PICT.OP_FILL_SAME_R_RECT: context.fillRoundRect(lastRectangle, ovSize.x, ovSize.y, fill); break; } // Do verbose mode output if (DEBUG) { switch (opCode) { case PICT.OP_FRAME_R_RECT: System.out.println("frameRRect: " + lastRectangle); break; case PICT.OP_PAINT_R_RECT: System.out.println("paintRRect: " + lastRectangle); break; case PICT.OP_ERASE_R_RECT: System.out.println("eraseRRect: " + lastRectangle); break; case PICT.OP_INVERT_R_RECT: System.out.println("invertRRect: " + lastRectangle); break; case PICT.OP_FILL_R_RECT: System.out.println("fillRRect: " + lastRectangle); break; case PICT.OP_FRAME_SAME_R_RECT: System.out.println("frameSameRRect: " + lastRectangle); break; case PICT.OP_PAINT_SAME_R_RECT: System.out.println("paintSameRRect: " + lastRectangle); break; case PICT.OP_ERASE_SAME_R_RECT: System.out.println("eraseSameRRect: " + lastRectangle); break; case PICT.OP_INVERT_SAME_R_RECT: System.out.println("invertSameRRect: " + lastRectangle); break; case PICT.OP_FILL_SAME_R_RECT: System.out.println("fillSameRRect: " + lastRectangle); 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, lastRectangle); 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: context.frameOval(lastRectangle); break; case PICT.OP_PAINT_OVAL: case PICT.OP_PAINT_SAME_OVAL: context.paintOval(lastRectangle); break; case PICT.OP_ERASE_OVAL: case PICT.OP_ERASE_SAME_OVAL: context.eraseOval(lastRectangle); break; case PICT.OP_INVERT_OVAL: case PICT.OP_INVERT_SAME_OVAL: context.invertOval(lastRectangle); break; case PICT.OP_FILL_OVAL: case PICT.OP_FILL_SAME_OVAL: context.fillOval(lastRectangle, fill); break; } // Do verbose mode output if (DEBUG) { switch (opCode) { case PICT.OP_FRAME_OVAL: System.out.println("frameOval: " + lastRectangle); break; case PICT.OP_PAINT_OVAL: System.out.println("paintOval: " + lastRectangle); break; case PICT.OP_ERASE_OVAL: System.out.println("eraseOval: " + lastRectangle); break; case PICT.OP_INVERT_OVAL: System.out.println("invertOval: " + lastRectangle); break; case PICT.OP_FILL_OVAL: System.out.println("fillOval: " + lastRectangle); break; case PICT.OP_FRAME_SAME_OVAL: System.out.println("frameSameOval: " + lastRectangle); break; case PICT.OP_PAINT_SAME_OVAL: System.out.println("paintSameOval: " + lastRectangle); break; case PICT.OP_ERASE_SAME_OVAL: System.out.println("eraseSameOval: " + lastRectangle); break; case PICT.OP_INVERT_SAME_OVAL: System.out.println("invertSameOval: " + lastRectangle); break; case PICT.OP_FILL_SAME_OVAL: System.out.println("fillSameOval: " + lastRectangle); 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, lastRectangle); 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: context.frameArc(lastRectangle, arcAngles.x, arcAngles.y); break; case PICT.OP_PAINT_ARC: case PICT.OP_PAINT_SAME_ARC: context.paintArc(lastRectangle, arcAngles.x, arcAngles.y); break; case PICT.OP_ERASE_ARC: case PICT.OP_ERASE_SAME_ARC: context.eraseArc(lastRectangle, arcAngles.x, arcAngles.y); break; case PICT.OP_INVERT_ARC: case PICT.OP_INVERT_SAME_ARC: context.invertArc(lastRectangle, arcAngles.x, arcAngles.y); break; case PICT.OP_FILL_ARC: case PICT.OP_FILL_SAME_ARC: context.fillArc(lastRectangle, arcAngles.x, arcAngles.y, fill); break; } // Do verbose mode output if (DEBUG) { switch (opCode) { case PICT.OP_FRAME_ARC: System.out.println("frameArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_PAINT_ARC: System.out.println("paintArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_ERASE_ARC: System.out.println("eraseArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_INVERT_ARC: System.out.println("invertArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_FILL_ARC: System.out.println("fillArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_FRAME_SAME_ARC: System.out.println("frameSameArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_PAINT_SAME_ARC: System.out.println("paintSameArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_ERASE_SAME_ARC: System.out.println("eraseSameArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_INVERT_SAME_ARC: System.out.println("invertSameArc: " + lastRectangle + ", angles:" + arcAngles); break; case PICT.OP_FILL_SAME_ARC: System.out.println("fillSameArc: " + lastRectangle + ", 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: context.framePoly(polygon); break; case PICT.OP_PAINT_POLY: case PICT.OP_PAINT_SAME_POLY: context.paintPoly(polygon); break; case PICT.OP_ERASE_POLY: case PICT.OP_ERASE_SAME_POLY: context.erasePoly(polygon); break; case PICT.OP_INVERT_POLY: case PICT.OP_INVERT_SAME_POLY: context.invertPoly(polygon); break; case PICT.OP_FILL_POLY: case PICT.OP_FILL_SAME_POLY: context.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 0x7d: case 0x7e: case 0x7f: if (DEBUG) { System.out.println(String.format("%s: 0x%04x", PICT.APPLE_USE_RESERVED_FIELD, opCode)); } 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: context.frameRegion(new Area(region)); break; case PICT.OP_PAINT_RGN: case PICT.OP_PAINT_SAME_RGN: context.paintRegion(new Area(region)); break; case PICT.OP_ERASE_RGN: case PICT.OP_ERASE_SAME_RGN: context.eraseRegion(new Area(region)); break; case PICT.OP_INVERT_RGN: case PICT.OP_INVERT_SAME_RGN: context.invertRegion(new Area(region)); break; case PICT.OP_FILL_RGN: case PICT.OP_FILL_SAME_RGN: context.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 & 0x7FFF; // 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! 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(); 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); context.copyBits(image, rect, dstRect, mode, 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 - TODO"); } 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 - length: %d", PICT.APPLE_USE_RESERVED_FIELD, opCode, dataLength)); } break; case PICT.OP_PACK_BITS_RECT: readOpPackBitsRect(pStream, pixmapCount++); 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, 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 byte[] shortComment = new byte[2]; pStream.readFully(shortComment, 0, 2); if (DEBUG) { System.out.println("Short comment: " + Arrays.toString(shortComment)); } break; case PICT.OP_LONG_COMMENT:// NOTHING TO DO, JUST JUMP OVER /*byte[] longComment =*/ readLongComment(pStream); // TODO: Don't just skip... // https://developer.apple.com/legacy/library/documentation/mac/pdf/Imaging_With_QuickDraw/Appendix_B.pdf // Long comments can be used for PhotoShop IRBs (kind 498) or ICC profiles (224) and other meta data... // if (DEBUG) { // System.out.println("Long comment: " + Arrays.toString(longComment)); // } 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 { // TODO: We could issue a warning and return instead? In any case, can't continue, as we don't know the length of the opcode... // return; 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) { String pos; try { pos = String.format("position %d", imageInput.getStreamPosition()); } catch (IOException ignore) { pos = "unknown position"; } throw new IIOException(String.format("Error in PICT format: Unexpected end of File at %s", pos), e); } catch (IOException e) { throw new IIOException(String.format("Error in PICT format: %s", 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) { context.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(final ImageInputStream pStream, final int pPixmapCount) throws IOException { // Get rowBytes int rowBytesRaw = pStream.readUnsignedShort(); // System.out.println(String.format("%08d: 0x%04x", pStream.getStreamPosition(), rowBytesRaw)); // TODO: This way to determine pixmap vs bitmap is for version 2 only! int rowBytes = rowBytesRaw & 0x7FFF; boolean isPixMap = (rowBytesRaw & 0x8000) > 0; if (DEBUG) { System.out.print("packBitsRect, rowBytes: " + rowBytes); if (isPixMap) { 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! // TODO: ...or then again...? :-) Rectangle bounds = new Rectangle(); int y = pStream.readUnsignedShort(); int x = pStream.readUnsignedShort(); bounds.setLocation(x, y); y = pStream.readUnsignedShort(); x = pStream.readUnsignedShort(); bounds.setSize(x - bounds.x, y - bounds.y); if (DEBUG) { System.out.print(", bounds: " + bounds); } ColorModel colorModel; int cmpSize; if (isPixMap) { // Get PixMap record version number int pmVersion = pStream.readUnsignedShort(); if (DEBUG) { System.out.print(", pmVersion: " + pmVersion); } // Get packing format int packType = pStream.readUnsignedShort(); if (DEBUG) { System.out.print(", packType: " + packType); } // Get size of packed data (not used for v2) int packSize = pStream.readInt(); // TODO: Probably not int for BitMap (value seems too high)? 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 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 int clutId = pStream.readInt(); if (DEBUG) { System.out.println(", clutId:" + clutId); } // Reserved pStream.readInt(); // TODO: Seems to be packType 0 all the time? // packType = 0 means default.... if (packType != 0) { throw new IIOException("Unknown pack type: " + packType); } if (pixelType != 0) { throw new IIOException("Unsupported pixel type: " + pixelType); } // Color table colorModel = PICTUtil.readColorTable(pStream, pixelSize); } else { // Old style BitMap record cmpSize = 1; colorModel = QuickDraw.MONOCHROME; } // Get source rectangle. We DO NOT scale the coordinates by the // resolution info, since we are in pixmap coordinates here // TODO: readReactangleNonScaled() 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.println(", mode: " + transferMode); } // Set up pixel buffer for the RGB values byte[] pixArray = new byte[srcRect.height * rowBytes]; int pixBufOffset = 0; // Read in the RGB arrays for (int scanline = 0; scanline < srcRect.height; scanline++) { // Read in the scanline if (rowBytes > 8) { // Get byteCount of the scanline int packedBytesCount = rowBytes > 250 ? pStream.readUnsignedShort() : pStream.readUnsignedByte(); // Unpack them all Decoder decoder = new PackBitsDecoder(); DataInput unPackBits = new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pStream, packedBytesCount), decoder)); unPackBits.readFully(pixArray, pixBufOffset, rowBytes); } else { // Uncompressed imageInput.readFully(pixArray, pixBufOffset, rowBytes); } // Increment pixel buffer offset pixBufOffset += rowBytes; //////////////////////////////////////////////////// // TODO: This works for single image PICTs only... // However, this is the most common case. Ok for now processImageProgress(scanline * 100 / srcRect.height); if (abortRequested()) { processReadAborted(); // Skip rest of image data for (int skip = scanline + 1; skip < srcRect.height; skip++) { // Get byteCount of the scanline int packedBytesCount; if (rowBytes <= 8) { packedBytesCount = rowBytes; } else if (rowBytes > 250) { packedBytesCount = pStream.readUnsignedShort(); } else { packedBytesCount = pStream.readUnsignedByte(); } pStream.readFully(new byte[packedBytesCount], 0, packedBytesCount); if (DEBUG) { 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 (images.size() <= pPixmapCount) { // Create BufferedImage and add buffer it for multiple reads DataBuffer db = new DataBufferByte(pixArray, pixArray.length); WritableRaster raster = Raster.createPackedRaster(db, (rowBytes * 8) / cmpSize, srcRect.height, cmpSize, null); // TODO: last param should ideally be srcRect.getLocation() BufferedImage img = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null); images.add(img); } // Draw the image BufferedImage img = images.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.. context.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 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 java.io.IOException if an I/O error occurs while reading the image. */ private void readOpDirectBitsRect(final ImageInputStream pStream, final int pPixmapCount) throws IOException { if (DEBUG) { System.out.println("directBitsRect"); } // Skip PixMap pointer (always 0x000000FF); pStream.readInt(); // Get rowBytes int rowBytesRaw = pStream.readUnsignedShort(); int rowBytes = rowBytesRaw & 0x7FFF; 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! // TODO: ...or then again...? :-) Rectangle bounds = new Rectangle(); int y = pStream.readUnsignedShort(); int x = pStream.readUnsignedShort(); bounds.setLocation(x, y); y = pStream.readUnsignedShort(); x = pStream.readUnsignedShort(); bounds.setSize(x - bounds.x, y - bounds.y); if (DEBUG) { System.out.print(", bounds: " + bounds); } // Get PixMap record version number int pmVersion = pStream.readUnsignedShort(); if (DEBUG) { System.out.print(", pmVersion: " + pmVersion); } // Get packing format int packType = pStream.readUnsignedShort(); 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); } // 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 || packType == 2 || packType == 3) { dstBytes = new byte[rowBytes]; } else if (packType == 4) { dstBytes = new byte[cmpCount * rowBytes / 4]; } else { throw new IIOException("Unknown pack type: " + packType); } int[] pixArray = null; short[] shortArray = null; if (packType == 3) { shortArray = new short[srcRect.height * (rowBytes + 1) / 2]; } else { pixArray = new int[srcRect.height * (rowBytes + 3) / 4]; } int pixBufOffset = 0; int packedBytesCount; for (int scanline = 0; scanline < srcRect.height; scanline++) { // Read in the scanline if (packType > 2) { // Get byteCount of the scanline if (rowBytes > 250) { packedBytesCount = pStream.readUnsignedShort(); } else { packedBytesCount = pStream.readUnsignedByte(); } if (DEBUG) { System.out.print("Line " + scanline + ", byteCount: " + packedBytesCount); System.out.print(" dstBytes: " + dstBytes.length); System.out.println(); } // Unpack them all Decoder decoder = packType == 3 ? new PackBitsDecoder(2, false) : new PackBitsDecoder(); DataInput unPackBits = new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pStream, packedBytesCount), decoder, dstBytes.length)); unPackBits.readFully(dstBytes); } else { imageInput.readFully(dstBytes); } if (packType == 3) { // TYPE_USHORT_555_RGB for 16 bit for (int i = 0; i < srcRect.width; i++) { shortArray[pixBufOffset + i] = (short) (((0xff & dstBytes[2 * i]) << 8) | (0xff & dstBytes[2 * i + 1])); } } else { if (cmpCount == 3) { // RGB for (int i = 0; i < srcRect.width; i++) { // Set alpha values to all opaque pixArray[pixBufOffset + i] = 0xFF000000 // Get red values | (dstBytes[/*0* bounds.width*/i] & 0xFF) << 16 // Get green values | (dstBytes[/**/bounds.width + i] & 0xFF) << 8 // Get blue values | (dstBytes[2 * bounds.width + i] & 0xFF); } } else { // ARGB for (int i = 0; i < srcRect.width; i++) { // Get alpha values pixArray[pixBufOffset + i] = (dstBytes[/*0* bounds.width*/i] & 0xFF) << 24 // Get red values | (dstBytes[/**/bounds.width + i] & 0xFF) << 16 // Get green values | (dstBytes[2 * bounds.width + i] & 0xFF) << 8 // Get blue values | (dstBytes[3 * bounds.width + i] & 0xFF); } } } // Increment pixel buffer offset pixBufOffset += srcRect.width; //////////////////////////////////////////////////// // TODO: This works for single image PICTs only... // However, this is the most common case. Ok for now processImageProgress(scanline * 100 / srcRect.height); if (abortRequested()) { processReadAborted(); // Skip rest of image data for (int skip = scanline + 1; skip < srcRect.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 (images.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, srcRect.width, srcRect.height, srcRect.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, srcRect.width, srcRect.height, srcRect.width, cm.getMasks(), null); // TODO: last param should ideally be srcRect.getLocation() } BufferedImage img = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null); images.add(img); } // Draw the image BufferedImage img = images.get(pPixmapCount); if (img != null) { // TODO: FixMe.. Something wrong here, might be the copyBits methods. srcRect.setLocation(0, 0); // should not require this line.. context.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 byte[] readLongComment(final DataInput pStream) throws IOException { // Comment kind and data byte count short kind = pStream.readShort(); int length = pStream.readUnsignedShort(); if (DEBUG) { System.err.println("Long comment: " + kind + ", " + length + " bytes"); } // Get as many bytes as indicated by byte count byte[] bytes = new byte[length]; pStream.readFully(bytes, 0, length); return bytes; } /* * 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 / screenImageXRatio); } /* * 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 / screenImageYRatio); } /* * 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(final int pIndex, final ImageReadParam pParam) throws IOException { checkBounds(pIndex); processImageStarted(pIndex); // TODO: Param handling // TODO: Real subsampling for bit/pixmap/QT stills final int subX, subY; if (pParam != null) { subX = pParam.getSourceXSubsampling(); subY = pParam.getSourceYSubsampling(); } else { subX = 1; subY = 1; } Rectangle frame = getPICTFrame(); BufferedImage image = getDestination(pParam, getImageTypes(pIndex), getXPtCoord(frame.width), getYPtCoord(frame.height)); Graphics2D g = image.createGraphics(); try { // Might need to clear background g.setComposite(AlphaComposite.Src); g.setColor(new Color(0x00ffffff, true)); // Transparent white // g.setColor(Color.WHITE); g.fillRect(0, 0, image.getWidth(), image.getHeight()); AffineTransform instance = new AffineTransform(); if (pParam != null && pParam.getSourceRegion() != null) { Rectangle rectangle = pParam.getSourceRegion(); instance.translate(-rectangle.x, -rectangle.y); } instance.scale(screenImageXRatio / subX, screenImageYRatio / subY); g.setTransform(instance); // 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( ImageTypeSpecifiers.createPacked( ColorSpace.getInstance(ColorSpace.CS_sRGB), 0xff0000, 0xff00, 0xff, 0xff000000, DataBuffer.TYPE_INT, false ) ).iterator(); } protected static void showIt(final BufferedImage pImage, final String pTitle) { ImageReaderBase.showIt(pImage, pTitle); } public static void main(final String[] pArgs) throws IOException { ImageReader reader = new PICTImageReader(new PICTImageReaderSpi()); for (String arg : pArgs) { File file = new File(arg); try { ImageInputStream input = ImageIO.createImageInputStream(file); String title = file.getName(); System.out.println("canRead: " + reader.getOriginatingProvider().canDecodeInput(input)); reader.setInput(input); // BufferedImage image = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0)); // ImageReadParam param = reader.getDefaultReadParam(); // param.setDestination(image); long start = System.currentTimeMillis(); BufferedImage image = reader.read(0); System.out.println("time: " + (System.currentTimeMillis() - start)); showIt(image, title); System.out.println("image = " + image); } catch (IOException e) { System.err.println("Could not read " + file.getAbsolutePath() + ": " + e); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy