mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 12:35:29 -04:00
Merge pull request #426 from ckovorodkin/camera-raw-cr2-slice
CR2ImageReader: unslice result of JPEGLosslessDecoder
This commit is contained in:
commit
7ff74465d3
@ -36,6 +36,8 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.Slice;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.SliceContext;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
@ -432,17 +434,20 @@ public final class CR2ImageReader extends ImageReaderBase {
|
||||
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||
long[] slices = getValueAsLongArray(50752, "Slices", true);
|
||||
|
||||
// Format of this array, is slices[0] = N, slices[1] = slice0.width ... slices[N + 1] = sliceN.width
|
||||
if (slices[0] != slices.length - 2) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(slices));
|
||||
try {
|
||||
final Slice slice = Slice.createSlice(slices);
|
||||
SliceContext.set(slice);
|
||||
|
||||
// TODO: Get correct dimensions (sensor size?)
|
||||
int width = getWidth(0);
|
||||
int height = getHeight(0);
|
||||
|
||||
imageInput.seek(stripOffsets);
|
||||
return ImageIO.read(new SubImageInputStream(imageInput, stripByteCounts));
|
||||
} finally {
|
||||
SliceContext.remove();
|
||||
}
|
||||
// TODO: We really have multiple slices...
|
||||
|
||||
// TODO: Get correct dimensions (sensor size?)
|
||||
int width = getWidth(0);
|
||||
int height = getHeight(0);
|
||||
|
||||
imageInput.seek(stripOffsets);
|
||||
// byte[] data = new LosslessJPEGDecoder().decompress(new SubImageInputStream(imageInput, stripByteCounts), null);
|
||||
//
|
||||
// // TODO: We really have 2 bytes/sample
|
||||
@ -477,7 +482,7 @@ public final class CR2ImageReader extends ImageReaderBase {
|
||||
for (int i = 0; i < numImages; i++) {
|
||||
int numThumbnails = reader.getNumThumbnails(i);
|
||||
for (int n = 0; n < numThumbnails; n++) {
|
||||
showIt(reader.readThumbnail(i, n), arg + " image thumbnail" + n);
|
||||
showIt(reader.readThumbnail(i, n), arg + " image " + i + " thumbnail " + n);
|
||||
}
|
||||
|
||||
showIt(reader.read(i), arg + " image " + i);
|
||||
|
@ -66,10 +66,7 @@ final class JPEGLosslessDecoder {
|
||||
private int xLoc;
|
||||
private int yLoc;
|
||||
private int mask;
|
||||
private int[] outputData;
|
||||
private int[] outputRedData;
|
||||
private int[] outputGreenData;
|
||||
private int[] outputBlueData;
|
||||
private int[][] outputData;
|
||||
|
||||
private static final int IDCT_P[] = {
|
||||
0, 5, 40, 16, 45, 2, 7, 42,
|
||||
@ -146,8 +143,6 @@ final class JPEGLosslessDecoder {
|
||||
|
||||
int[][] decode() throws IOException {
|
||||
int current, scanNum = 0;
|
||||
final int pred[] = new int[10];
|
||||
int[][] outputRef;
|
||||
|
||||
xLoc = 0;
|
||||
yLoc = 0;
|
||||
@ -216,32 +211,27 @@ final class JPEGLosslessDecoder {
|
||||
xDim = frame.samplesPerLine;
|
||||
yDim = frame.lines;
|
||||
|
||||
outputRef = new int[numComp][];
|
||||
outputData = new int[numComp][];
|
||||
|
||||
// TODO: Support 4 components (RGBA/YCCA/CMYK/YCCK), others?
|
||||
if (numComp == 1) {
|
||||
outputData = new int[xDim * yDim];
|
||||
outputRef[0] = outputData;
|
||||
for (int componentIndex = 0; componentIndex < numComp; ++componentIndex) {
|
||||
// not a good use of memory, but I had trouble packing bytes into int. some values exceeded 255.
|
||||
outputData[componentIndex] = new int[xDim * yDim];
|
||||
}
|
||||
else {
|
||||
outputRedData = new int[xDim * yDim]; // not a good use of memory, but I had trouble packing bytes into int. some values exceeded 255.
|
||||
outputGreenData = new int[xDim * yDim];
|
||||
outputBlueData = new int[xDim * yDim];
|
||||
|
||||
outputRef[0] = outputRedData;
|
||||
outputRef[1] = outputGreenData;
|
||||
outputRef[2] = outputBlueData;
|
||||
final int firstValue[] = new int[numComp];
|
||||
for (int i = 0; i < numComp; i++) {
|
||||
firstValue[i] = (1 << (precision - 1));
|
||||
}
|
||||
|
||||
final int pred[] = new int[numComp];
|
||||
|
||||
scanNum++;
|
||||
|
||||
while (true) { // Decode one scan
|
||||
int temp[] = new int[1]; // to store remainder bits
|
||||
int index[] = new int[1];
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
pred[i] = (1 << (precision - 1));
|
||||
}
|
||||
System.arraycopy(firstValue, 0, pred, 0, numComp);
|
||||
|
||||
if (restartInterval == 0) {
|
||||
current = decode(pred, temp, index);
|
||||
@ -283,9 +273,10 @@ final class JPEGLosslessDecoder {
|
||||
readNumber();
|
||||
current = input.readUnsignedShort();
|
||||
}
|
||||
// TODO oe: 05.05.2018 Is it correct loop? Content of outputData from previous iteration is always lost.
|
||||
} while ((current != JPEG.EOI) && ((xLoc < xDim) && (yLoc < yDim)) && (scanNum == 0));
|
||||
|
||||
return outputRef;
|
||||
return outputData;
|
||||
}
|
||||
|
||||
private void processWarningOccured(String warning) {
|
||||
@ -339,7 +330,7 @@ final class JPEGLosslessDecoder {
|
||||
return decodeRGB(prev, temp, index);
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
return decodeAny(prev, temp, index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,6 +342,7 @@ final class JPEGLosslessDecoder {
|
||||
prev[0] = (1 << (frame.samplePrecision - 1));
|
||||
}
|
||||
else {
|
||||
final int[] outputData = this.outputData[0];
|
||||
switch (selection) {
|
||||
case 2:
|
||||
prev[0] = getPreviousY(outputData);
|
||||
@ -397,6 +389,9 @@ final class JPEGLosslessDecoder {
|
||||
}
|
||||
|
||||
private int decodeRGB(final int prev[], final int temp[], final int index[]) throws IOException {
|
||||
final int[] outputRedData = outputData[0];
|
||||
final int[] outputGreenData = outputData[1];
|
||||
final int[] outputBlueData = outputData[2];
|
||||
switch (selection) {
|
||||
case 2:
|
||||
prev[0] = getPreviousY(outputRedData);
|
||||
@ -435,6 +430,43 @@ final class JPEGLosslessDecoder {
|
||||
break;
|
||||
}
|
||||
|
||||
return decode0(prev, temp, index);
|
||||
}
|
||||
|
||||
private int decodeAny(final int prev[], final int temp[], final int index[]) throws IOException {
|
||||
for (int componentIndex = 0; componentIndex < outputData.length; ++componentIndex) {
|
||||
final int[] outputData = this.outputData[componentIndex];
|
||||
final int previous;
|
||||
switch (selection) {
|
||||
case 2:
|
||||
previous = getPreviousY(outputData);
|
||||
break;
|
||||
case 3:
|
||||
previous = getPreviousXY(outputData);
|
||||
break;
|
||||
case 4:
|
||||
previous = (getPreviousX(outputData) + getPreviousY(outputData)) - getPreviousXY(outputData);
|
||||
break;
|
||||
case 5:
|
||||
previous = getPreviousX(outputData) + ((getPreviousY(outputData) - getPreviousXY(outputData)) >> 1);
|
||||
break;
|
||||
case 6:
|
||||
previous = getPreviousY(outputData) + ((getPreviousX(outputData) - getPreviousXY(outputData)) >> 1);
|
||||
break;
|
||||
case 7:
|
||||
previous = (int) (((long) getPreviousX(outputData) + getPreviousY(outputData)) / 2);
|
||||
break;
|
||||
default:
|
||||
previous = getPreviousX(outputData);
|
||||
break;
|
||||
}
|
||||
prev[componentIndex] = previous;
|
||||
}
|
||||
|
||||
return decode0(prev, temp, index);
|
||||
}
|
||||
|
||||
private int decode0(int[] prev, int[] temp, int[] index) throws IOException {
|
||||
int value, actab[], dctab[];
|
||||
int qtab[];
|
||||
|
||||
@ -692,14 +724,17 @@ final class JPEGLosslessDecoder {
|
||||
if (numComp == 1) {
|
||||
outputSingle(pred);
|
||||
}
|
||||
else {
|
||||
else if (numComp == 3) {
|
||||
outputRGB(pred);
|
||||
}
|
||||
else {
|
||||
outputAny(pred);
|
||||
}
|
||||
}
|
||||
|
||||
private void outputSingle(final int pred[]) {
|
||||
if ((xLoc < xDim) && (yLoc < yDim)) {
|
||||
outputData[(yLoc * xDim) + xLoc] = mask & pred[0];
|
||||
outputData[0][(yLoc * xDim) + xLoc] = mask & pred[0];
|
||||
xLoc++;
|
||||
|
||||
if (xLoc >= xDim) {
|
||||
@ -711,9 +746,25 @@ final class JPEGLosslessDecoder {
|
||||
|
||||
private void outputRGB(final int pred[]) {
|
||||
if ((xLoc < xDim) && (yLoc < yDim)) {
|
||||
outputRedData[(yLoc * xDim) + xLoc] = pred[0];
|
||||
outputGreenData[(yLoc * xDim) + xLoc] = pred[1];
|
||||
outputBlueData[(yLoc * xDim) + xLoc] = pred[2];
|
||||
final int index = (yLoc * xDim) + xLoc;
|
||||
outputData[0][index] = pred[0];
|
||||
outputData[1][index] = pred[1];
|
||||
outputData[2][index] = pred[2];
|
||||
xLoc++;
|
||||
|
||||
if (xLoc >= xDim) {
|
||||
yLoc++;
|
||||
xLoc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void outputAny(final int pred[]) {
|
||||
if ((xLoc < xDim) && (yLoc < yDim)) {
|
||||
final int index = (yLoc * xDim) + xLoc;
|
||||
for (int componentIndex = 0; componentIndex < outputData.length; ++componentIndex) {
|
||||
outputData[componentIndex][index] = pred[componentIndex];
|
||||
}
|
||||
xLoc++;
|
||||
|
||||
if (xLoc >= xDim) {
|
||||
|
@ -86,6 +86,22 @@ final class JPEGLosslessDecoderWrapper {
|
||||
int width = decoder.getDimX();
|
||||
int height = decoder.getDimY();
|
||||
|
||||
if (SliceContext.isPresent()) { //QnD
|
||||
final int[][] unsliced = new int[1][];
|
||||
final int componentCount = decoder.getNumComponents();
|
||||
final Slice slice = SliceContext.get();
|
||||
unsliced[0] = slice.unslice(decoded, componentCount, height);
|
||||
switch (decoder.getPrecision()) {
|
||||
case 8:
|
||||
return to8Bit1ComponentGrayScale(unsliced, width * componentCount, height);
|
||||
case 10:
|
||||
case 12:
|
||||
case 14:
|
||||
case 16:
|
||||
return to16Bit1ComponentGrayScale(unsliced, decoder.getPrecision(), width * componentCount, height);
|
||||
}
|
||||
}
|
||||
|
||||
// Single component, assumed to be Gray
|
||||
if (decoder.getNumComponents() == 1) {
|
||||
switch (decoder.getPrecision()) {
|
||||
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) 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.jpeg;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* For CR2 RAW image.
|
||||
*
|
||||
* @author Oleg Ermolaev Date: 05.05.2018 2:04
|
||||
*/
|
||||
public class Slice {
|
||||
public static final int FIRST_WIDTH_COUNT_INDEX = 0;
|
||||
public static final int FIRST_WIDTH_INDEX = 1;
|
||||
public static final int LAST_WIDTH_INDEX = 2;
|
||||
|
||||
private final int firstWidthCount;
|
||||
private final int firstWidth;
|
||||
private final int lastWidth;
|
||||
|
||||
public Slice(int firstWidthCount, int firstWidth, int lastWidth) {
|
||||
this.firstWidthCount = firstWidthCount;
|
||||
this.firstWidth = firstWidth;
|
||||
this.lastWidth = lastWidth;
|
||||
}
|
||||
|
||||
public static Slice createSlice(long[] values) throws IIOException {
|
||||
if (values == null || values.length != 3) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
final long firstWidthCount = values[FIRST_WIDTH_COUNT_INDEX];
|
||||
final long firstWidth = values[FIRST_WIDTH_INDEX];
|
||||
final long lastWidth = values[LAST_WIDTH_INDEX];
|
||||
if (!(0 < firstWidthCount && firstWidthCount <= Integer.MAX_VALUE) ||
|
||||
!(0 < firstWidth && firstWidth <= Integer.MAX_VALUE) ||
|
||||
!(0 < lastWidth && lastWidth <= Integer.MAX_VALUE) ||
|
||||
firstWidthCount * firstWidth + lastWidth > Integer.MAX_VALUE) {
|
||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(values));
|
||||
}
|
||||
return new Slice((int) firstWidthCount, (int) firstWidth, (int) lastWidth);
|
||||
}
|
||||
|
||||
public int getFirstWidthCount() {
|
||||
return firstWidthCount;
|
||||
}
|
||||
|
||||
public int getFirstWidth() {
|
||||
return firstWidth;
|
||||
}
|
||||
|
||||
public int getLastWidth() {
|
||||
return lastWidth;
|
||||
}
|
||||
|
||||
private int getWidth() {
|
||||
return firstWidthCount * firstWidth + lastWidth;
|
||||
}
|
||||
|
||||
public int[] unslice(int[][] data, int componentCount, int height) throws IIOException {
|
||||
final int width = getWidth();
|
||||
final int[] result = new int[width * height];
|
||||
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
if (result.length != data[componentIndex].length * componentCount) {
|
||||
throw new IIOException(String.format("Invalid array size for component #%d", componentIndex));
|
||||
}
|
||||
}
|
||||
|
||||
int position = 0;
|
||||
int currentWidth = firstWidth / componentCount;
|
||||
for (int sliceIndex = 0; sliceIndex < firstWidthCount + 1; ++sliceIndex) {
|
||||
if (sliceIndex == firstWidthCount) {
|
||||
currentWidth = lastWidth / componentCount;
|
||||
}
|
||||
final int sliceOffset = sliceIndex * firstWidth;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
final int yOffset = y * width;
|
||||
for (int x = 0; x < currentWidth; ++x) {
|
||||
final int xOffset = x * componentCount;
|
||||
for (int componentIndex = 0; componentIndex < componentCount; componentIndex++) {
|
||||
result[sliceOffset + yOffset + xOffset + componentIndex] = data[componentIndex][position];
|
||||
}
|
||||
position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oleg Ermolaev
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) 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.jpeg;
|
||||
|
||||
/**
|
||||
* QnD
|
||||
*
|
||||
* @author Oleg Ermolaev Date: 05.05.2018 2:13
|
||||
*/
|
||||
public class SliceContext {
|
||||
private static final ThreadLocal<Slice> CONTEXT = new ThreadLocal<>();
|
||||
|
||||
public static boolean isPresent() {
|
||||
return get() != null;
|
||||
}
|
||||
|
||||
public static Slice get() {
|
||||
return CONTEXT.get();
|
||||
}
|
||||
|
||||
public static void set(Slice slice) {
|
||||
CONTEXT.set(slice);
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
CONTEXT.remove();
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
short point = pStream.readShort();
|
||||
|
||||
if (nextIsV) {
|
||||
if (point == 0x7fff) {
|
||||
// Done
|
||||
break;
|
||||
}
|
||||
|
||||
y = point;
|
||||
nextIsV = false;
|
||||
}
|
||||
else {
|
||||
if (point == 0x7fff) {
|
||||
nextIsV = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
x = point;
|
||||
polygon.addPoint(x, y);
|
||||
}
|
||||
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();
|
||||
|
||||
return polygon;
|
||||
boolean nextIsVertical = true;
|
||||
|
||||
int x, y = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
short point = pStream.readShort();
|
||||
|
||||
if (nextIsVertical) {
|
||||
if (point == 0x7fff) {
|
||||
// Done
|
||||
break;
|
||||
}
|
||||
|
||||
y = point;
|
||||
nextIsVertical = false;
|
||||
}
|
||||
else {
|
||||
if (point == 0x7fff) {
|
||||
nextIsVertical = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
x = point;
|
||||
area.exclusiveOr(new Area(new Rectangle(x, y, pBounds.x + pBounds.width - x, pBounds.y + pBounds.height - y)));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
274
imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/QuickDrawContext.java
Executable file → Normal file
274
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}.
|
||||
@ -170,7 +169,7 @@ class QuickDrawContext {
|
||||
// Sets the text's font style (0..255)
|
||||
void setTextFace(final int face) {
|
||||
int style = 0;
|
||||
if ((face & QuickDraw.TX_BOLD_MASK) > 0) {
|
||||
if ((face & QuickDraw.TX_BOLD_MASK) > 0) {
|
||||
style |= Font.BOLD;
|
||||
}
|
||||
if ((face & QuickDraw.TX_ITALIC_MASK) > 0) {
|
||||
@ -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.
|
||||
*/
|
||||
@ -575,8 +586,8 @@ class QuickDrawContext {
|
||||
* the graphics pen.
|
||||
*
|
||||
* @param pRectangle the rectangle to frame
|
||||
* @param pArcW width of the oval defining the rounded corner.
|
||||
* @param pArcH height of the oval defining the rounded corner.
|
||||
* @param pArcW width of the oval defining the rounded corner.
|
||||
* @param pArcH height of the oval defining the rounded corner.
|
||||
*/
|
||||
public void frameRoundRect(final Rectangle2D pRectangle, int pArcW, int pArcH) {
|
||||
frameShape(toRoundRect(pRectangle, pArcW, pArcH));
|
||||
@ -587,8 +598,8 @@ class QuickDrawContext {
|
||||
* graphics pen, using the pattern mode of the graphics pen.
|
||||
*
|
||||
* @param pRectangle the rectangle to paint
|
||||
* @param pArcW width of the oval defining the rounded corner.
|
||||
* @param pArcH height of the oval defining the rounded corner.
|
||||
* @param pArcW width of the oval defining the rounded corner.
|
||||
* @param pArcH height of the oval defining the rounded corner.
|
||||
*/
|
||||
public void paintRoundRect(final Rectangle2D pRectangle, int pArcW, int pArcH) {
|
||||
paintShape(toRoundRect(pRectangle, pArcW, pArcH));
|
||||
@ -694,75 +705,75 @@ class QuickDrawContext {
|
||||
/**
|
||||
* Converts a rectangle to an arc.
|
||||
*
|
||||
* @param pRectangle the framing rectangle
|
||||
* @param pRectangle the framing rectangle
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
* @param pClosed specifies if the arc should be closed
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
* @param pClosed specifies if the arc should be closed
|
||||
* @return the arc
|
||||
*/
|
||||
private static Arc2D.Double toArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle, final boolean pClosed) {
|
||||
return new Arc2D.Double(pRectangle, 90 - pStartAngle, -pArcAngle, pClosed ? Arc2D.PIE : Arc2D.OPEN);
|
||||
}
|
||||
private static Arc2D.Double toArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle, final boolean pClosed) {
|
||||
return new Arc2D.Double(pRectangle, 90 - pStartAngle, -pArcAngle, pClosed ? Arc2D.PIE : Arc2D.OPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* FrameArc(r,int,int) // outline arc with the size, pattern, and pattern mode of
|
||||
* the graphics pen.
|
||||
*
|
||||
* @param pRectangle the rectangle to frame
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
*/
|
||||
public void frameArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) {
|
||||
frameShape(toArc(pRectangle, pStartAngle, pArcAngle, false));
|
||||
}
|
||||
* FrameArc(r,int,int) // outline arc with the size, pattern, and pattern mode of
|
||||
* the graphics pen.
|
||||
*
|
||||
* @param pRectangle the rectangle to frame
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
*/
|
||||
public void frameArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) {
|
||||
frameShape(toArc(pRectangle, pStartAngle, pArcAngle, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* PaintArc(r,int,int) // fills an arc's interior with the pattern of the
|
||||
* graphics pen, using the pattern mode of the graphics pen.
|
||||
*
|
||||
* @param pRectangle the rectangle to paint
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
*/
|
||||
public void paintArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) {
|
||||
paintShape(toArc(pRectangle, pStartAngle, pArcAngle, true));
|
||||
}
|
||||
* PaintArc(r,int,int) // fills an arc's interior with the pattern of the
|
||||
* graphics pen, using the pattern mode of the graphics pen.
|
||||
*
|
||||
* @param pRectangle the rectangle to paint
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
*/
|
||||
public void paintArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) {
|
||||
paintShape(toArc(pRectangle, pStartAngle, pArcAngle, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* FillArc(r,int,int, pat) // fills an arc's interior with any pattern you
|
||||
* specify. The procedure transfers the pattern with the patCopy pattern
|
||||
* mode, which directly copies your requested pattern into the shape.
|
||||
*
|
||||
* @param pRectangle the rectangle to fill
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
* @param pPattern the pattern to use
|
||||
*/
|
||||
public void fillArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle, Pattern pPattern) {
|
||||
fillShape(toArc(pRectangle, pStartAngle, pArcAngle, true), pPattern);
|
||||
}
|
||||
* FillArc(r,int,int, pat) // fills an arc's interior with any pattern you
|
||||
* specify. The procedure transfers the pattern with the patCopy pattern
|
||||
* mode, which directly copies your requested pattern into the shape.
|
||||
*
|
||||
* @param pRectangle the rectangle to fill
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
* @param pPattern the pattern to use
|
||||
*/
|
||||
public void fillArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle, Pattern pPattern) {
|
||||
fillShape(toArc(pRectangle, pStartAngle, pArcAngle, true), pPattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* EraseArc(r,int,int) // fills the arc's interior with the background pattern
|
||||
*
|
||||
* @param pRectangle the rectangle to erase
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
*/
|
||||
public void eraseArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) {
|
||||
eraseShape(toArc(pRectangle, pStartAngle, pArcAngle, true));
|
||||
}
|
||||
* EraseArc(r,int,int) // fills the arc's interior with the background pattern
|
||||
*
|
||||
* @param pRectangle the rectangle to erase
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
*/
|
||||
public void eraseArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) {
|
||||
eraseShape(toArc(pRectangle, pStartAngle, pArcAngle, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* InvertArc(r,int,int) // reverses the color of all pixels in the arc
|
||||
*
|
||||
* @param pRectangle the rectangle to invert
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
*/
|
||||
public void invertArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) {
|
||||
invertShape(toArc(pRectangle, pStartAngle, pArcAngle, true));
|
||||
}
|
||||
* InvertArc(r,int,int) // reverses the color of all pixels in the arc
|
||||
*
|
||||
* @param pRectangle the rectangle to invert
|
||||
* @param pStartAngle start angle in degrees (starting from 12'o clock, this differs from Java)
|
||||
* @param pArcAngle rotation angle in degrees (starting from {@code pStartAngle}, this differs from Java arcs)
|
||||
*/
|
||||
public void invertArc(final Rectangle2D pRectangle, int pStartAngle, int pArcAngle) {
|
||||
invertShape(toArc(pRectangle, pStartAngle, pArcAngle, true));
|
||||
}
|
||||
|
||||
/*
|
||||
// http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-120.html#HEADING120-0
|
||||
@ -776,7 +787,7 @@ class QuickDrawContext {
|
||||
|
||||
// Drawing Polygons:
|
||||
// TODO: What is the Xxx2D equivalent of Polygon!? GeneralPath?
|
||||
// FramePoly
|
||||
// FramePoly
|
||||
public void framePoly(final Polygon pPolygon) {
|
||||
// TODO: The old PICTImageReader does not draw the last connection line,
|
||||
// unless the start and end point is the same...
|
||||
@ -905,18 +916,19 @@ class QuickDrawContext {
|
||||
|
||||
// Copying Images (SKIP?):
|
||||
*/
|
||||
/**
|
||||
* CopyBits.
|
||||
* <p/>
|
||||
* Note that the destination is always {@code this}.
|
||||
*
|
||||
* @param pSrcBitmap the source bitmap to copy pixels from
|
||||
* @param pSrcRect the source rectangle
|
||||
* @param pDstRect the destination rectangle
|
||||
* @param pMode the blending mode
|
||||
* @param pMaskRgn the mask region
|
||||
*/
|
||||
public void copyBits(BufferedImage pSrcBitmap, Rectangle pSrcRect, Rectangle pDstRect, int pMode, Shape pMaskRgn) {
|
||||
|
||||
/**
|
||||
* CopyBits.
|
||||
* <p/>
|
||||
* Note that the destination is always {@code this}.
|
||||
*
|
||||
* @param pSrcBitmap the source bitmap to copy pixels from
|
||||
* @param pSrcRect the source rectangle
|
||||
* @param pDstRect the destination rectangle
|
||||
* @param pMode the blending mode
|
||||
* @param pMaskRgn the mask region
|
||||
*/
|
||||
public void copyBits(BufferedImage pSrcBitmap, Rectangle pSrcRect, Rectangle pDstRect, int pMode, Shape pMaskRgn) {
|
||||
graphics.setComposite(getCompositeFor(pMode));
|
||||
if (pMaskRgn != null) {
|
||||
setClipRegion(pMaskRgn);
|
||||
@ -924,7 +936,7 @@ class QuickDrawContext {
|
||||
|
||||
graphics.drawImage(
|
||||
pSrcBitmap,
|
||||
pDstRect.x,
|
||||
pDstRect.x,
|
||||
pDstRect.y,
|
||||
pDstRect.x + pDstRect.width,
|
||||
pDstRect.y + pDstRect.height,
|
||||
@ -935,22 +947,34 @@ class QuickDrawContext {
|
||||
null
|
||||
);
|
||||
|
||||
setClipRegion(null);
|
||||
}
|
||||
setClipRegion(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* CopyMask
|
||||
*/
|
||||
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
|
||||
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) {
|
||||
|
||||
/**
|
||||
* CopyDeepMask -- available to basic QuickDraw only in System 7, combines the functionality of both CopyBits and CopyMask
|
||||
*/
|
||||
public void copyDeepMask(BufferedImage pSrcBitmap,
|
||||
BufferedImage pMaskBitmap,
|
||||
Rectangle pSrcRect,
|
||||
Rectangle pMaskRect,
|
||||
Rectangle pDstRect,
|
||||
int pSrcCopy,
|
||||
Shape pMaskRgn) {
|
||||
throw new UnsupportedOperationException("Method copyDeepMask not implemented"); // TODO: Implement
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Drawing With the Eight-Color System:
|
||||
@ -978,15 +1002,15 @@ class QuickDrawContext {
|
||||
DrawChar // draws the glyph of a single 1-byte character.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DrawString - draws the text of a Pascal string.
|
||||
*
|
||||
* @param pString a Pascal string (a string of length less than or equal to 255 chars).
|
||||
*/
|
||||
public void drawString(String pString) {
|
||||
setupForText();
|
||||
graphics.drawString(pString, (float) getPenPosition().getX(), (float) getPenPosition().getY());
|
||||
}
|
||||
/**
|
||||
* DrawString - draws the text of a Pascal string.
|
||||
*
|
||||
* @param pString a Pascal string (a string of length less than or equal to 255 chars).
|
||||
*/
|
||||
public void drawString(String pString) {
|
||||
setupForText();
|
||||
graphics.drawString(pString, (float) getPenPosition().getX(), (float) getPenPosition().getY());
|
||||
}
|
||||
|
||||
/*
|
||||
DrawText // draws the glyphs of a sequence of characters.
|
||||
@ -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