mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 12:05:29 -04:00
#423: Finally fixed region parsing to Area (seems to work, but slow)!
Bonus: Implemented more transfer modes, better QT parsing/debug output.
This commit is contained in:
parent
5c8b4e0edf
commit
ce7fb1cb94
@ -332,7 +332,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
*
|
||||
* @param pGraphics the graphics object to draw onto.
|
||||
*
|
||||
* @throws javax.imageio.IIOException if the data can not be read.
|
||||
* @throws 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 {
|
||||
@ -351,8 +351,8 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
*
|
||||
* @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.
|
||||
* @throws IIOException if the data can not be read.
|
||||
* @throws IOException if an I/O error occurs while reading the image.
|
||||
*/
|
||||
private void readPICTopcodes(ImageInputStream pStream) throws IOException {
|
||||
pStream.seek(imageStartStreamPos);
|
||||
@ -371,7 +371,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
String text;
|
||||
Rectangle bounds = new Rectangle();
|
||||
Polygon polygon = new Polygon();
|
||||
Polygon region = new Polygon();
|
||||
Area region = new Area();
|
||||
int pixmapCount = 0;
|
||||
|
||||
try {
|
||||
@ -1312,27 +1312,27 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
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) {
|
||||
if (region != null && !region.getBounds().isEmpty()) {
|
||||
switch (opCode) {
|
||||
case PICT.OP_FRAME_RGN:
|
||||
case PICT.OP_FRAME_SAME_RGN:
|
||||
context.frameRegion(new Area(region));
|
||||
context.frameRegion(region);
|
||||
break;
|
||||
case PICT.OP_PAINT_RGN:
|
||||
case PICT.OP_PAINT_SAME_RGN:
|
||||
context.paintRegion(new Area(region));
|
||||
context.paintRegion(region);
|
||||
break;
|
||||
case PICT.OP_ERASE_RGN:
|
||||
case PICT.OP_ERASE_SAME_RGN:
|
||||
context.eraseRegion(new Area(region));
|
||||
context.eraseRegion(region);
|
||||
break;
|
||||
case PICT.OP_INVERT_RGN:
|
||||
case PICT.OP_INVERT_SAME_RGN:
|
||||
context.invertRegion(new Area(region));
|
||||
context.invertRegion(region);
|
||||
break;
|
||||
case PICT.OP_FILL_RGN:
|
||||
case PICT.OP_FILL_SAME_RGN:
|
||||
context.fillRegion(new Area(region), fill);
|
||||
context.fillRegion(region, fill);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1395,29 +1395,10 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
// 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;
|
||||
*/
|
||||
readOpBits(pStream, false);
|
||||
break;
|
||||
|
||||
case PICT.OP_BITS_RGN:
|
||||
/*
|
||||
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;
|
||||
*/
|
||||
readOpBits(pStream, true);
|
||||
|
||||
break;
|
||||
@ -1486,10 +1467,6 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
|
||||
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;
|
||||
@ -1585,43 +1562,56 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
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);
|
||||
int version = pStream.readUnsignedShort();
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("...");
|
||||
System.out.print("compressedQuickTime");
|
||||
System.out.println(", size: " + dataLength + ", version: " + version);
|
||||
}
|
||||
|
||||
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));
|
||||
// Matrix
|
||||
int[] matrix = new int[9];
|
||||
for (int i = 0; i < matrix.length; i++) {
|
||||
matrix[i] = pStream.readInt();
|
||||
}
|
||||
if (DEBUG) {
|
||||
System.out.println(String.format("matrix: %s", Arrays.toString(matrix)));
|
||||
}
|
||||
|
||||
// Matte
|
||||
long matteSize = pStream.readUnsignedInt();
|
||||
Rectangle matteRect = new Rectangle();
|
||||
readRectangle(pStream, matteRect);
|
||||
|
||||
// Transfer mode
|
||||
int mode = pStream.readUnsignedShort();
|
||||
|
||||
// Read the source rectangle
|
||||
Rectangle srcRect = new Rectangle();
|
||||
readRectangle(pStream, srcRect);
|
||||
|
||||
// ...and more
|
||||
int accuracy = pStream.readInt();
|
||||
int maskSize = pStream.readInt();
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.print("matteSize: " + matteSize);
|
||||
System.out.print(", matteRect: " + matteRect);
|
||||
System.out.print(", mode: " + mode);
|
||||
System.out.print(", srcRect: " + srcRect);
|
||||
System.out.print(", accuracy: " + accuracy);
|
||||
System.out.println(", maskSize: " + maskSize);
|
||||
}
|
||||
|
||||
BufferedImage image = QuickTime.decompress(pStream);
|
||||
|
||||
if (image != null) {
|
||||
context.copyBits(image, new Rectangle(image.getWidth(), image.getHeight()), destination, QuickDraw.SRC_COPY, null);
|
||||
context.copyBits(image, srcRect, srcRect, mode, 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...
|
||||
// NOTE: This algorithm is reverse-engineered by looking at various test inputs and not from any spec...
|
||||
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
|
||||
@ -1803,10 +1793,15 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
Rectangle regionBounds = new Rectangle();
|
||||
Polygon region = hasRegion ? readRegion(pStream, regionBounds) : null;
|
||||
Area region = hasRegion ? readRegion(pStream, regionBounds) : null;
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println(hasRegion ? ", region: " + region : "" );
|
||||
if (hasRegion) {
|
||||
verboseRegionCmd(", region", regionBounds, region);
|
||||
}
|
||||
else {
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
// Set up pixel buffer for the RGB values
|
||||
@ -1882,8 +1877,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
BufferedImage img = images.get(pPixmapCount);
|
||||
if (img != null) {
|
||||
srcRect.setLocation(0, 0); // Raster always start at 0,0
|
||||
// dstRect.translate(-frame.x, -frame.y);
|
||||
context.copyBits(img, srcRect, dstRect, transferMode, null);
|
||||
context.copyBits(img, srcRect, dstRect, transferMode, region);
|
||||
}
|
||||
|
||||
// Line break at the end
|
||||
@ -1899,8 +1893,8 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
* @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.
|
||||
* @throws IIOException if the data can not be read.
|
||||
* @throws IOException if an I/O error occurs while reading the image.
|
||||
*/
|
||||
private void readOpDirectBits(final ImageInputStream pStream, final boolean hasRegion, final int pPixmapCount) throws IOException {
|
||||
// Skip PixMap pointer (always 0x000000FF);
|
||||
@ -2039,7 +2033,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
System.out.print(", mode: " + transferMode);
|
||||
}
|
||||
|
||||
Polygon region = hasRegion ? readRegion(pStream, new Rectangle()) : null;
|
||||
Area region = hasRegion ? readRegion(pStream, new Rectangle()) : null;
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println(hasRegion ? ", region: " + region : "");
|
||||
@ -2196,8 +2190,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
BufferedImage img = images.get(pPixmapCount);
|
||||
if (img != null) {
|
||||
srcRect.setLocation(0, 0); // Raster always starts at 0,0
|
||||
// dstRect.translate(-frame.x, -frame.y);
|
||||
context.copyBits(img, srcRect, dstRect, transferMode, null);
|
||||
context.copyBits(img, srcRect, dstRect, transferMode, region);
|
||||
}
|
||||
|
||||
// Line break at the end
|
||||
@ -2206,6 +2199,16 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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;
|
||||
*/
|
||||
private void readOpBits(ImageInputStream pStream, boolean hasRegion) throws IOException {
|
||||
// Get rowBytes
|
||||
int rowBytesRaw = pStream.readUnsignedShort();
|
||||
@ -2323,7 +2326,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
// Get transfer mode
|
||||
int mode = pStream.readUnsignedShort();
|
||||
|
||||
Polygon region = hasRegion ? readRegion(pStream, new Rectangle()) : null;
|
||||
Area region = hasRegion ? readRegion(pStream, new Rectangle()) : null;
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.print(", bounds: " + bounds);
|
||||
@ -2347,8 +2350,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
|
||||
// Draw pixel data
|
||||
srcRect.setLocation(0, 0); // Raster always start at 0,0
|
||||
// dstRect.translate(-frame.x, -frame.y);
|
||||
context.copyBits(image, srcRect, dstRect, mode, null);
|
||||
context.copyBits(image, srcRect, dstRect, mode, region);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2368,84 +2370,90 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
|
||||
pDestRect.setLocation(getXPtCoord(x), getYPtCoord(y));
|
||||
pDestRect.setSize(getXPtCoord(w - x), getYPtCoord(h - y));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in a region. The input stream should be positioned at the first byte
|
||||
* of the region. {@code pBoundsRect} is a rectangle that will be set to the
|
||||
* of the region. {@code pBounds} 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
|
||||
* @param pBounds the bounds rectangle to read into
|
||||
*
|
||||
* @return the polygon containing the region, or an empty polygon if the
|
||||
* @return the area containing the region, or an empty polygon if the
|
||||
* region is a rectangle.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs while reading the image.
|
||||
*/
|
||||
private Polygon readRegion(DataInput pStream, Rectangle pBoundsRect) throws IOException {
|
||||
// Get minimal region
|
||||
|
||||
private Area readRegion(DataInput pStream, Rectangle pBounds) throws IOException {
|
||||
// Get region data size
|
||||
int size = pStream.readUnsignedShort();
|
||||
|
||||
// Get region bounds
|
||||
int y = getYPtCoord(pStream.readShort());
|
||||
int x = getXPtCoord(pStream.readShort());
|
||||
pBoundsRect.setLocation(x, y);
|
||||
readRectangle(pStream, pBounds);
|
||||
|
||||
y = getYPtCoord(pStream.readShort()) - pBoundsRect.getLocation().y;
|
||||
x = getXPtCoord(pStream.readShort()) - pBoundsRect.getLocation().x;
|
||||
pBoundsRect.setSize(x, y);
|
||||
|
||||
Polygon polygon = new Polygon();
|
||||
int count = (size - 10) / 2;
|
||||
boolean nextIsV = true;
|
||||
|
||||
if (count == 0) {
|
||||
// Minimal region, just the bounds
|
||||
return new Area(pBounds);
|
||||
}
|
||||
else {
|
||||
// Normal region, parse inversion points and build area
|
||||
Area area = new Area();
|
||||
|
||||
boolean nextIsVertical = true;
|
||||
|
||||
int x, y = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
short point = pStream.readShort();
|
||||
|
||||
if (nextIsV) {
|
||||
if (nextIsVertical) {
|
||||
if (point == 0x7fff) {
|
||||
// Done
|
||||
break;
|
||||
}
|
||||
|
||||
y = point;
|
||||
nextIsV = false;
|
||||
nextIsVertical = false;
|
||||
}
|
||||
else {
|
||||
if (point == 0x7fff) {
|
||||
nextIsV = true;
|
||||
nextIsVertical = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
x = point;
|
||||
polygon.addPoint(x, y);
|
||||
area.exclusiveOr(new Area(new Rectangle(x, y, pBounds.x + pBounds.width - x, pBounds.y + pBounds.height - y)));
|
||||
}
|
||||
}
|
||||
|
||||
return polygon;
|
||||
if (!pBounds.contains(area.getBounds())) {
|
||||
processWarningOccurred("Bad region, contains point(s) out of bounds " + pBounds + ": " + area);
|
||||
}
|
||||
|
||||
/*
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in a polygon. The input stream should be positioned at the first byte
|
||||
* of the polygon.
|
||||
*
|
||||
* @param pStream the stream to read from
|
||||
* @param pBounds the bounds rectangle to read into
|
||||
*
|
||||
* @return the polygon
|
||||
*
|
||||
* @throws IOException if an I/O error occurs while reading the image.
|
||||
*/
|
||||
private Polygon readPoly(DataInput pStream, Rectangle pBoundsRect) throws IOException {
|
||||
private Polygon readPoly(DataInput pStream, Rectangle pBounds) 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);
|
||||
readRectangle(pStream, pBounds);
|
||||
|
||||
// Initialize the point array to the right size
|
||||
int points = (size - 10) / 4;
|
||||
@ -2454,11 +2462,15 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
Polygon polygon = new Polygon();
|
||||
|
||||
for (int i = 0; i < points; i++) {
|
||||
y = getYPtCoord(pStream.readShort());
|
||||
x = getXPtCoord(pStream.readShort());
|
||||
int y = getYPtCoord(pStream.readShort());
|
||||
int x = getXPtCoord(pStream.readShort());
|
||||
polygon.addPoint(x, y);
|
||||
}
|
||||
|
||||
if (!pBounds.contains(polygon.getBounds())) {
|
||||
processWarningOccurred("Bad poly, contains point(s) out of bounds " + pBounds + ": " + polygon);
|
||||
}
|
||||
|
||||
return polygon;
|
||||
}
|
||||
|
||||
@ -2540,15 +2552,15 @@ public final class PICTImageReader extends ImageReaderBase {
|
||||
/*
|
||||
* Write out region command, bounds and points.
|
||||
*/
|
||||
private void verboseRegionCmd(String pCmd, Rectangle pBounds, Polygon pPolygon) {
|
||||
private void verboseRegionCmd(String pCmd, Rectangle pBounds, Area pRegion) {
|
||||
System.out.println(pCmd + ": " + pBounds);
|
||||
System.out.print("Region points: ");
|
||||
if (pPolygon != null && pPolygon.npoints > 0) {
|
||||
System.out.print("(" + pPolygon.xpoints[0] + "," + pPolygon.ypoints[0] + ")");
|
||||
}
|
||||
for (int i = 1; pPolygon != null && i < pPolygon.npoints; i++) {
|
||||
System.out.print(", (" + pPolygon.xpoints[i] + "," + pPolygon.ypoints[i] + ")");
|
||||
}
|
||||
// System.out.print("Region points: ");
|
||||
// if (pPolygon != null && pPolygon.npoints > 0) {
|
||||
// System.out.print("(" + pPolygon.xpoints[0] + "," + pPolygon.ypoints[0] + ")");
|
||||
// }
|
||||
// for (int i = 1; pPolygon != null && i < pPolygon.npoints; i++) {
|
||||
// System.out.print(", (" + pPolygon.xpoints[i] + "," + pPolygon.ypoints[i] + ")");
|
||||
// }
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
|
78
imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDraw.java
Executable file → Normal file
78
imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDraw.java
Executable file → Normal file
@ -28,8 +28,9 @@
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pict;
|
||||
|
||||
import java.awt.image.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
/**
|
||||
* QuickDraw constants.
|
||||
@ -61,6 +62,18 @@ interface QuickDraw {
|
||||
// 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!
|
||||
/*
|
||||
Source mode Action on destination pixel
|
||||
If source pixel is black If source pixel is white If source pixel is any other color
|
||||
srcCopy Apply foreground color Apply background color Apply weighted portions of foreground and background colors
|
||||
notSrcCopy Apply background color Apply foreground color Apply weighted portions of background and foreground colors
|
||||
srcOr Apply foreground color Leave alone Apply weighted portions of foreground color
|
||||
notSrcOr Leave alone Apply foreground color Apply weighted portions of foreground color
|
||||
srcXor Invert (undefined for colored destination pixel) Leave alone Leave alone
|
||||
notSrcXor Leave alone Invert (undefined for colored destination pixel) Leave alone
|
||||
srcBic Apply background color Leave alone Apply weighted portions of background color
|
||||
notSrcBic Leave alone Apply background color Apply weighted portions of background color
|
||||
*/
|
||||
int SRC_COPY = 0;
|
||||
int SRC_OR = 1;
|
||||
int SRC_XOR = 2;
|
||||
@ -84,16 +97,73 @@ interface QuickDraw {
|
||||
|
||||
// Arithmetic Transfer Modes
|
||||
// http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-199.html#HEADING199-112
|
||||
/*
|
||||
CONST
|
||||
blend = 32; {replace destination pixel with a blend }
|
||||
{ of the source and destination pixel }
|
||||
{ colors; if the destination is a bitmap or }
|
||||
{ 1-bit pixel map, revert to srcCopy mode}
|
||||
addPin = 33; {replace destination pixel with the sum of }
|
||||
{ the source and destination pixel colors-- }
|
||||
{ up to a maximum allowable value; if }
|
||||
{ the destination is a bitmap or }
|
||||
{ 1-bit pixel map, revert to srcBic mode}
|
||||
addOver = 34; {replace destination pixel with the sum of }
|
||||
{ the source and destination pixel colors-- }
|
||||
{ but if the value of the red, green, or }
|
||||
{ blue component exceeds 65,536, then }
|
||||
{ subtract 65,536 from that value; if the }
|
||||
{ destination is a bitmap or 1-bit }
|
||||
{ pixel map, revert to srcXor mode}
|
||||
subPin = 35; {replace destination pixel with the }
|
||||
{ difference of the source and destination }
|
||||
{ pixel colors--but not less than a minimum }
|
||||
{ allowable value; if the destination }
|
||||
{ is a bitmap or 1-bit pixel map, revert to }
|
||||
{ srcOr mode}
|
||||
transparent = 36; {replace the destination pixel with the }
|
||||
{ source pixel if the source pixel isn't }
|
||||
{ equal to the background color}
|
||||
addMax = 37; {compare the source and destination pixels, }
|
||||
{ and replace the destination pixel with }
|
||||
{ the color containing the greater }
|
||||
{ saturation of each of the RGB components; }
|
||||
{ if the destination is a bitmap or }
|
||||
{ 1-bit pixel map, revert to srcBic mode}
|
||||
subOver = 38; {replace destination pixel with the }
|
||||
{ difference of the source and destination }
|
||||
{ pixel colors--but if the value of a red, }
|
||||
{ green, or blue component is }
|
||||
{ less than 0, add the negative result to }
|
||||
{ 65,536; if the destination is a bitmap or }
|
||||
{ 1-bit pixel map, revert to srcXor mode}
|
||||
adMin = 39; {compare the source and destination pixels, }
|
||||
{ and replace the destination pixel with }
|
||||
{ the color containing the lesser }
|
||||
{ saturation of each of the RGB components; }
|
||||
{ if the destination is a bitmap or }
|
||||
{ 1-bit pixel map, revert to srcOr mode}
|
||||
*/
|
||||
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 ADD_MAX = 37;
|
||||
int SUB_OVER = 38;
|
||||
int AD_MIN = 39;
|
||||
int ADD_MIN = 39;
|
||||
|
||||
// Transfer mode for text only
|
||||
/*
|
||||
If the destination device is color and grayishTextOr is the transfer mode,
|
||||
QuickDraw draws with a blend of the foreground and background colors. If
|
||||
the destination device is black and white, the grayishTextOr mode dithers
|
||||
black and white.
|
||||
|
||||
Note that grayishTextOr is not considered a standard transfer mode because
|
||||
currently it is not stored in pictures, and printing with it is undefined.
|
||||
*/
|
||||
int GRAYISH_TEXT_OR = 49;
|
||||
// int MASK = 64; // ?! From Käry's code..
|
||||
|
||||
/*
|
||||
* Text face masks.
|
||||
|
@ -0,0 +1,156 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pict;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
/**
|
||||
* QuickDrawComposite
|
||||
*/
|
||||
interface QuickDrawComposite extends Composite {
|
||||
|
||||
QuickDrawComposite NotSrcXor = new NotSrcXor();
|
||||
QuickDrawComposite AddMax = new AddMax();
|
||||
QuickDrawComposite AddMin = new AddMin();
|
||||
|
||||
class NotSrcXor implements QuickDrawComposite {
|
||||
|
||||
// TODO: Src can probably be any color model that can be encoded in PICT, dst is always RGB/TYPE_INT
|
||||
public CompositeContext createContext(final ColorModel srcColorModel, final ColorModel dstColorModel, RenderingHints hints) {
|
||||
{
|
||||
if (!srcColorModel.getColorSpace().isCS_sRGB() || !dstColorModel.getColorSpace().isCS_sRGB()) {
|
||||
throw new IllegalArgumentException("Only sRGB supported");
|
||||
}
|
||||
}
|
||||
|
||||
return new CompositeContext() {
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
|
||||
int width = min(src.getWidth(), dstIn.getWidth());
|
||||
|
||||
// We always work in RGB, using DataBuffer.TYPE_INT transfer type.
|
||||
int[] srcData = null;
|
||||
int[] dstData = null;
|
||||
int[] resData = new int[width - src.getMinX()];
|
||||
|
||||
for (int y = src.getMinY(); y < src.getHeight(); y++) {
|
||||
srcData = (int[]) src.getDataElements(src.getMinX(), y, width, 1, srcData);
|
||||
dstData = (int[]) dstIn.getDataElements(src.getMinX(), y, width, 1, dstData);
|
||||
|
||||
for (int x = src.getMinX(); x < width; x++) {
|
||||
// TODO: Decide how to handle alpha (if at all)
|
||||
resData[x] = 0xff000000 | ((~srcData[x] ^ dstData[x])) & 0xffffff;
|
||||
}
|
||||
|
||||
dstOut.setDataElements(src.getMinX(), y, width, 1, resData);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class AddMax implements QuickDrawComposite {
|
||||
// TODO: Src can probably be any color model that can be encoded in PICT, dst is always RGB/TYPE_INT
|
||||
public CompositeContext createContext(final ColorModel srcColorModel, final ColorModel dstColorModel, RenderingHints hints) {
|
||||
{
|
||||
if (!srcColorModel.getColorSpace().isCS_sRGB() || !dstColorModel.getColorSpace().isCS_sRGB()) {
|
||||
throw new IllegalArgumentException("Only sRGB supported");
|
||||
}
|
||||
}
|
||||
|
||||
return new CompositeContext() {
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
|
||||
int width = min(src.getWidth(), dstIn.getWidth());
|
||||
|
||||
// We always work in RGB, using DataBuffer.TYPE_INT transfer type.
|
||||
int[] srcData = null;
|
||||
int[] dstData = null;
|
||||
|
||||
int[] resData = new int[width - src.getMinX()];
|
||||
|
||||
for (int y = src.getMinY(); y < src.getHeight(); y++) {
|
||||
srcData = (int[]) src.getDataElements(src.getMinX(), y, width, 1, srcData);
|
||||
dstData = (int[]) dstIn.getDataElements(src.getMinX(), y, width, 1, dstData);
|
||||
|
||||
for (int x = src.getMinX(); x < width; x++) {
|
||||
int sAlpha = (srcData[x] >>> 24) & 0xFF;
|
||||
int sRed = sAlpha * ((srcData[x] >> 16) & 0xFF) / 0xFF;
|
||||
int sGreen = sAlpha * ((srcData[x] >> 8) & 0xFF) / 0xFF;
|
||||
int sBlue = sAlpha * ((srcData[x]) & 0xFF) / 0xFF;
|
||||
|
||||
int dAlpha = (dstData[x] >>> 24) & 0xFF;
|
||||
int dRed = dAlpha * ((dstData[x] >> 16) & 0xFF) / 0xFF;
|
||||
int dGreen = dAlpha * ((dstData[x] >> 8) & 0xFF) / 0xFF;
|
||||
int dBlue = dAlpha * ((dstData[x]) & 0xFF) / 0xFF;
|
||||
|
||||
resData[x] = (max(sAlpha, dAlpha) << 24)
|
||||
| (max(sRed, dRed) << 16)
|
||||
| (max(sGreen, dGreen) << 8)
|
||||
| (max(sBlue, dBlue));
|
||||
}
|
||||
|
||||
dstOut.setDataElements(src.getMinX(), y, width, 1, resData);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class AddMin implements QuickDrawComposite {
|
||||
// TODO: Src can probably be any color model that can be encoded in PICT, dst is always RGB/TYPE_INT
|
||||
public CompositeContext createContext(final ColorModel srcColorModel, final ColorModel dstColorModel, RenderingHints hints) {
|
||||
{
|
||||
if (!srcColorModel.getColorSpace().isCS_sRGB() || !dstColorModel.getColorSpace().isCS_sRGB()) {
|
||||
throw new IllegalArgumentException("Only sRGB supported");
|
||||
}
|
||||
}
|
||||
|
||||
return new CompositeContext() {
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
|
||||
int width = min(src.getWidth(), dstIn.getWidth());
|
||||
|
||||
// We always work in RGB, using DataBuffer.TYPE_INT transfer type.
|
||||
int[] srcData = null;
|
||||
int[] dstData = null;
|
||||
int[] resData = new int[width - src.getMinX()];
|
||||
|
||||
for (int y = src.getMinY(); y < src.getHeight(); y++) {
|
||||
srcData = (int[]) src.getDataElements(src.getMinX(), y, width, 1, srcData);
|
||||
dstData = (int[]) dstIn.getDataElements(src.getMinX(), y, width, 1, dstData);
|
||||
|
||||
for (int x = src.getMinX(); x < width; x++) {
|
||||
int sAlpha = (srcData[x] >>> 24) & 0xFF;
|
||||
int sRed = sAlpha * ((srcData[x] >> 16) & 0xFF) / 0xFF;
|
||||
int sGreen = sAlpha * ((srcData[x] >> 8) & 0xFF) / 0xFF;
|
||||
int sBlue = sAlpha * ((srcData[x]) & 0xFF) / 0xFF;
|
||||
|
||||
int dAlpha = (dstData[x] >>> 24) & 0xFF;
|
||||
int dRed = dAlpha * ((dstData[x] >> 16) & 0xFF) / 0xFF;
|
||||
int dGreen = dAlpha * ((dstData[x] >> 8) & 0xFF) / 0xFF;
|
||||
int dBlue = dAlpha * ((dstData[x]) & 0xFF) / 0xFF;
|
||||
|
||||
resData[x] = (min(sAlpha, dAlpha) << 24)
|
||||
| (min(sRed, dRed) << 16)
|
||||
| (min(sGreen, dGreen) << 8)
|
||||
| (min(sBlue, dBlue));
|
||||
}
|
||||
|
||||
dstOut.setDataElements(src.getMinX(), y, width, 1, resData);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
88
imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDrawContext.java
Executable file → Normal file
88
imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDrawContext.java
Executable file → Normal file
@ -33,9 +33,8 @@ import com.twelvemonkeys.lang.Validate;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
import static java.lang.Math.sqrt;
|
||||
|
||||
/**
|
||||
* Emulates an Apple QuickDraw rendering context, backed by a Java {@link Graphics2D}.
|
||||
@ -308,9 +307,9 @@ class QuickDrawContext {
|
||||
case QuickDraw.ADD_OVER:
|
||||
case QuickDraw.SUB_PIN:
|
||||
case QuickDraw.TRANSPARENT:
|
||||
case QuickDraw.AD_MAX:
|
||||
case QuickDraw.ADD_MAX:
|
||||
case QuickDraw.SUB_OVER:
|
||||
case QuickDraw.AD_MIN:
|
||||
case QuickDraw.ADD_MIN:
|
||||
case QuickDraw.GRAYISH_TEXT_OR:
|
||||
penMode = pPenMode;
|
||||
break;
|
||||
@ -365,12 +364,11 @@ class QuickDrawContext {
|
||||
case QuickDraw.SRC_BIC:
|
||||
return AlphaComposite.Clear;
|
||||
case QuickDraw.NOT_SRC_XOR:
|
||||
return new NotSrcXor();
|
||||
return QuickDrawComposite.NotSrcXor;
|
||||
case QuickDraw.NOT_SRC_COPY:
|
||||
case QuickDraw.NOT_SRC_OR:
|
||||
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
|
||||
@ -385,8 +383,22 @@ class QuickDrawContext {
|
||||
case QuickDraw.NOT_PAT_XOR:
|
||||
case QuickDraw.NOT_PAT_BIC:
|
||||
throw new UnsupportedOperationException("Not implemented for mode " + pMode);
|
||||
// return null;
|
||||
// TODO: Aritmetic transfer modes
|
||||
// Aritmetic transfer modes
|
||||
case QuickDraw.BLEND:
|
||||
return AlphaComposite.SrcOver.derive(.5f);
|
||||
case QuickDraw.ADD_PIN:
|
||||
case QuickDraw.ADD_OVER:
|
||||
case QuickDraw.SUB_PIN:
|
||||
case QuickDraw.TRANSPARENT:
|
||||
throw new UnsupportedOperationException("Not implemented for mode " + pMode);
|
||||
case QuickDraw.ADD_MAX:
|
||||
return QuickDrawComposite.AddMax;
|
||||
case QuickDraw.SUB_OVER:
|
||||
throw new UnsupportedOperationException("Not implemented for mode " + pMode);
|
||||
case QuickDraw.ADD_MIN:
|
||||
return QuickDrawComposite.AddMin;
|
||||
case QuickDraw.GRAYISH_TEXT_OR:
|
||||
throw new UnsupportedOperationException("Not implemented for mode " + pMode);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown pnMode: " + pMode);
|
||||
@ -401,7 +413,6 @@ class QuickDrawContext {
|
||||
graphics.setComposite(getCompositeFor(textMode));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up context for line drawing/painting.
|
||||
*/
|
||||
@ -905,6 +916,7 @@ class QuickDrawContext {
|
||||
|
||||
// Copying Images (SKIP?):
|
||||
*/
|
||||
|
||||
/**
|
||||
* CopyBits.
|
||||
* <p/>
|
||||
@ -941,14 +953,26 @@ class QuickDrawContext {
|
||||
/**
|
||||
* CopyMask
|
||||
*/
|
||||
public void copyMask(BufferedImage pSrcBitmap, BufferedImage pMaskBitmap, Rectangle pSrcRect, Rectangle pMaskRect, Rectangle pDstRect, int pSrcCopy, Shape pMaskRgn) {
|
||||
public void copyMask(BufferedImage pSrcBitmap,
|
||||
BufferedImage pMaskBitmap,
|
||||
Rectangle pSrcRect,
|
||||
Rectangle pMaskRect,
|
||||
Rectangle pDstRect,
|
||||
int pSrcCopy,
|
||||
Shape pMaskRgn) {
|
||||
throw new UnsupportedOperationException("Method copyMask 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) {
|
||||
public void copyDeepMask(BufferedImage pSrcBitmap,
|
||||
BufferedImage pMaskBitmap,
|
||||
Rectangle pSrcRect,
|
||||
Rectangle pMaskRect,
|
||||
Rectangle pDstRect,
|
||||
int pSrcCopy,
|
||||
Shape pMaskRgn) {
|
||||
throw new UnsupportedOperationException("Method copyDeepMask not implemented"); // TODO: Implement
|
||||
}
|
||||
|
||||
@ -1079,7 +1103,7 @@ class QuickDrawContext {
|
||||
thisY = points[1];
|
||||
float dx = thisX - lastX;
|
||||
float dy = thisY - lastY;
|
||||
float distance = (float) Math.sqrt(dx * dx + dy * dy);
|
||||
float distance = (float) sqrt(dx * dx + dy * dy);
|
||||
if (distance >= next) {
|
||||
float r = 1.0f / distance;
|
||||
//float angle = (float) Math.atan2(dy, dx);
|
||||
@ -1106,43 +1130,5 @@ class QuickDrawContext {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class NotSrcXor implements Composite {
|
||||
// TODO: Src can probably be any color model that can be encoded in PICT, dst is always RGB/TYPE_INT
|
||||
public CompositeContext createContext(final ColorModel srcColorModel, final ColorModel dstColorModel, RenderingHints hints) {
|
||||
{
|
||||
if (!srcColorModel.getColorSpace().isCS_sRGB() || !dstColorModel.getColorSpace().isCS_sRGB()) {
|
||||
throw new IllegalArgumentException("Only sRGB supported");
|
||||
}
|
||||
}
|
||||
|
||||
return new CompositeContext() {
|
||||
public void dispose() {
|
||||
|
||||
}
|
||||
|
||||
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
|
||||
// We always work in RGB, using DataBuffer.TYPE_INT transfer type.
|
||||
int[] srcData = null;
|
||||
int[] dstData = null;
|
||||
int[] resData = new int[src.getWidth() - src.getMinX()];
|
||||
|
||||
for (int y = src.getMinY(); y < src.getHeight(); y++) {
|
||||
srcData = (int[]) src.getDataElements(src.getMinX(), y, src.getWidth(), 1, srcData);
|
||||
dstData = (int[]) dstIn.getDataElements(src.getMinX(), y, src.getWidth(), 1, dstData);
|
||||
|
||||
for (int x = src.getMinX(); x < src.getWidth(); x++) {
|
||||
// TODO: Decide how to handle alpha (if at all)
|
||||
resData[x] = 0xff000000 | ((~ srcData[x] ^ dstData[x])) & 0xffffff ;
|
||||
// resData[x] = ~ srcData[x] ^ dstData[x];
|
||||
}
|
||||
|
||||
dstOut.setDataElements(src.getMinX(), y, src.getWidth(), 1, resData);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user