mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-06 04:55:30 -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.jpeg.JPEGSegmentUtil;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
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 com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
@ -432,17 +434,20 @@ public final class CR2ImageReader extends ImageReaderBase {
|
|||||||
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
int stripByteCounts = getValueAsInt(TIFF.TAG_STRIP_BYTE_COUNTS, "StripByteCounts");
|
||||||
long[] slices = getValueAsLongArray(50752, "Slices", true);
|
long[] slices = getValueAsLongArray(50752, "Slices", true);
|
||||||
|
|
||||||
// Format of this array, is slices[0] = N, slices[1] = slice0.width ... slices[N + 1] = sliceN.width
|
try {
|
||||||
if (slices[0] != slices.length - 2) {
|
final Slice slice = Slice.createSlice(slices);
|
||||||
throw new IIOException("Unexpected slices array: " + Arrays.toString(slices));
|
SliceContext.set(slice);
|
||||||
}
|
|
||||||
// TODO: We really have multiple slices...
|
|
||||||
|
|
||||||
// TODO: Get correct dimensions (sensor size?)
|
// TODO: Get correct dimensions (sensor size?)
|
||||||
int width = getWidth(0);
|
int width = getWidth(0);
|
||||||
int height = getHeight(0);
|
int height = getHeight(0);
|
||||||
|
|
||||||
imageInput.seek(stripOffsets);
|
imageInput.seek(stripOffsets);
|
||||||
|
return ImageIO.read(new SubImageInputStream(imageInput, stripByteCounts));
|
||||||
|
} finally {
|
||||||
|
SliceContext.remove();
|
||||||
|
}
|
||||||
|
|
||||||
// byte[] data = new LosslessJPEGDecoder().decompress(new SubImageInputStream(imageInput, stripByteCounts), null);
|
// byte[] data = new LosslessJPEGDecoder().decompress(new SubImageInputStream(imageInput, stripByteCounts), null);
|
||||||
//
|
//
|
||||||
// // TODO: We really have 2 bytes/sample
|
// // TODO: We really have 2 bytes/sample
|
||||||
@ -477,7 +482,7 @@ public final class CR2ImageReader extends ImageReaderBase {
|
|||||||
for (int i = 0; i < numImages; i++) {
|
for (int i = 0; i < numImages; i++) {
|
||||||
int numThumbnails = reader.getNumThumbnails(i);
|
int numThumbnails = reader.getNumThumbnails(i);
|
||||||
for (int n = 0; n < numThumbnails; n++) {
|
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);
|
showIt(reader.read(i), arg + " image " + i);
|
||||||
|
@ -66,10 +66,7 @@ final class JPEGLosslessDecoder {
|
|||||||
private int xLoc;
|
private int xLoc;
|
||||||
private int yLoc;
|
private int yLoc;
|
||||||
private int mask;
|
private int mask;
|
||||||
private int[] outputData;
|
private int[][] outputData;
|
||||||
private int[] outputRedData;
|
|
||||||
private int[] outputGreenData;
|
|
||||||
private int[] outputBlueData;
|
|
||||||
|
|
||||||
private static final int IDCT_P[] = {
|
private static final int IDCT_P[] = {
|
||||||
0, 5, 40, 16, 45, 2, 7, 42,
|
0, 5, 40, 16, 45, 2, 7, 42,
|
||||||
@ -146,8 +143,6 @@ final class JPEGLosslessDecoder {
|
|||||||
|
|
||||||
int[][] decode() throws IOException {
|
int[][] decode() throws IOException {
|
||||||
int current, scanNum = 0;
|
int current, scanNum = 0;
|
||||||
final int pred[] = new int[10];
|
|
||||||
int[][] outputRef;
|
|
||||||
|
|
||||||
xLoc = 0;
|
xLoc = 0;
|
||||||
yLoc = 0;
|
yLoc = 0;
|
||||||
@ -216,32 +211,27 @@ final class JPEGLosslessDecoder {
|
|||||||
xDim = frame.samplesPerLine;
|
xDim = frame.samplesPerLine;
|
||||||
yDim = frame.lines;
|
yDim = frame.lines;
|
||||||
|
|
||||||
outputRef = new int[numComp][];
|
outputData = new int[numComp][];
|
||||||
|
|
||||||
// TODO: Support 4 components (RGBA/YCCA/CMYK/YCCK), others?
|
for (int componentIndex = 0; componentIndex < numComp; ++componentIndex) {
|
||||||
if (numComp == 1) {
|
// not a good use of memory, but I had trouble packing bytes into int. some values exceeded 255.
|
||||||
outputData = new int[xDim * yDim];
|
outputData[componentIndex] = new int[xDim * yDim];
|
||||||
outputRef[0] = outputData;
|
|
||||||
}
|
}
|
||||||
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;
|
final int firstValue[] = new int[numComp];
|
||||||
outputRef[1] = outputGreenData;
|
for (int i = 0; i < numComp; i++) {
|
||||||
outputRef[2] = outputBlueData;
|
firstValue[i] = (1 << (precision - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int pred[] = new int[numComp];
|
||||||
|
|
||||||
scanNum++;
|
scanNum++;
|
||||||
|
|
||||||
while (true) { // Decode one scan
|
while (true) { // Decode one scan
|
||||||
int temp[] = new int[1]; // to store remainder bits
|
int temp[] = new int[1]; // to store remainder bits
|
||||||
int index[] = new int[1];
|
int index[] = new int[1];
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
System.arraycopy(firstValue, 0, pred, 0, numComp);
|
||||||
pred[i] = (1 << (precision - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (restartInterval == 0) {
|
if (restartInterval == 0) {
|
||||||
current = decode(pred, temp, index);
|
current = decode(pred, temp, index);
|
||||||
@ -283,9 +273,10 @@ final class JPEGLosslessDecoder {
|
|||||||
readNumber();
|
readNumber();
|
||||||
current = input.readUnsignedShort();
|
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));
|
} while ((current != JPEG.EOI) && ((xLoc < xDim) && (yLoc < yDim)) && (scanNum == 0));
|
||||||
|
|
||||||
return outputRef;
|
return outputData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processWarningOccured(String warning) {
|
private void processWarningOccured(String warning) {
|
||||||
@ -339,7 +330,7 @@ final class JPEGLosslessDecoder {
|
|||||||
return decodeRGB(prev, temp, index);
|
return decodeRGB(prev, temp, index);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return -1;
|
return decodeAny(prev, temp, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,6 +342,7 @@ final class JPEGLosslessDecoder {
|
|||||||
prev[0] = (1 << (frame.samplePrecision - 1));
|
prev[0] = (1 << (frame.samplePrecision - 1));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
final int[] outputData = this.outputData[0];
|
||||||
switch (selection) {
|
switch (selection) {
|
||||||
case 2:
|
case 2:
|
||||||
prev[0] = getPreviousY(outputData);
|
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 {
|
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) {
|
switch (selection) {
|
||||||
case 2:
|
case 2:
|
||||||
prev[0] = getPreviousY(outputRedData);
|
prev[0] = getPreviousY(outputRedData);
|
||||||
@ -435,6 +430,43 @@ final class JPEGLosslessDecoder {
|
|||||||
break;
|
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 value, actab[], dctab[];
|
||||||
int qtab[];
|
int qtab[];
|
||||||
|
|
||||||
@ -692,14 +724,17 @@ final class JPEGLosslessDecoder {
|
|||||||
if (numComp == 1) {
|
if (numComp == 1) {
|
||||||
outputSingle(pred);
|
outputSingle(pred);
|
||||||
}
|
}
|
||||||
else {
|
else if (numComp == 3) {
|
||||||
outputRGB(pred);
|
outputRGB(pred);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
outputAny(pred);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void outputSingle(final int pred[]) {
|
private void outputSingle(final int pred[]) {
|
||||||
if ((xLoc < xDim) && (yLoc < yDim)) {
|
if ((xLoc < xDim) && (yLoc < yDim)) {
|
||||||
outputData[(yLoc * xDim) + xLoc] = mask & pred[0];
|
outputData[0][(yLoc * xDim) + xLoc] = mask & pred[0];
|
||||||
xLoc++;
|
xLoc++;
|
||||||
|
|
||||||
if (xLoc >= xDim) {
|
if (xLoc >= xDim) {
|
||||||
@ -711,9 +746,25 @@ final class JPEGLosslessDecoder {
|
|||||||
|
|
||||||
private void outputRGB(final int pred[]) {
|
private void outputRGB(final int pred[]) {
|
||||||
if ((xLoc < xDim) && (yLoc < yDim)) {
|
if ((xLoc < xDim) && (yLoc < yDim)) {
|
||||||
outputRedData[(yLoc * xDim) + xLoc] = pred[0];
|
final int index = (yLoc * xDim) + xLoc;
|
||||||
outputGreenData[(yLoc * xDim) + xLoc] = pred[1];
|
outputData[0][index] = pred[0];
|
||||||
outputBlueData[(yLoc * xDim) + xLoc] = pred[2];
|
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++;
|
xLoc++;
|
||||||
|
|
||||||
if (xLoc >= xDim) {
|
if (xLoc >= xDim) {
|
||||||
|
@ -86,6 +86,22 @@ final class JPEGLosslessDecoderWrapper {
|
|||||||
int width = decoder.getDimX();
|
int width = decoder.getDimX();
|
||||||
int height = decoder.getDimY();
|
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
|
// Single component, assumed to be Gray
|
||||||
if (decoder.getNumComponents() == 1) {
|
if (decoder.getNumComponents() == 1) {
|
||||||
switch (decoder.getPrecision()) {
|
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.
|
* @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.
|
* @throws IOException if an I/O error occurs while reading the image.
|
||||||
*/
|
*/
|
||||||
private void drawOnto(Graphics2D pGraphics) throws IOException {
|
private void drawOnto(Graphics2D pGraphics) throws IOException {
|
||||||
@ -351,8 +351,8 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
*
|
*
|
||||||
* @param pStream the stream to read from
|
* @param pStream the stream to read from
|
||||||
*
|
*
|
||||||
* @throws javax.imageio.IIOException if the data can not be read.
|
* @throws IIOException if the data can not be read.
|
||||||
* @throws java.io.IOException if an I/O error occurs while reading the image.
|
* @throws IOException if an I/O error occurs while reading the image.
|
||||||
*/
|
*/
|
||||||
private void readPICTopcodes(ImageInputStream pStream) throws IOException {
|
private void readPICTopcodes(ImageInputStream pStream) throws IOException {
|
||||||
pStream.seek(imageStartStreamPos);
|
pStream.seek(imageStartStreamPos);
|
||||||
@ -371,7 +371,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
String text;
|
String text;
|
||||||
Rectangle bounds = new Rectangle();
|
Rectangle bounds = new Rectangle();
|
||||||
Polygon polygon = new Polygon();
|
Polygon polygon = new Polygon();
|
||||||
Polygon region = new Polygon();
|
Area region = new Area();
|
||||||
int pixmapCount = 0;
|
int pixmapCount = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1312,27 +1312,27 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
case PICT.OP_INVERT_SAME_RGN:// OK, not tested
|
case PICT.OP_INVERT_SAME_RGN:// OK, not tested
|
||||||
case PICT.OP_FILL_SAME_RGN:// OK, not tested
|
case PICT.OP_FILL_SAME_RGN:// OK, not tested
|
||||||
// Draw
|
// Draw
|
||||||
if (region != null && region.npoints > 1) {
|
if (region != null && !region.getBounds().isEmpty()) {
|
||||||
switch (opCode) {
|
switch (opCode) {
|
||||||
case PICT.OP_FRAME_RGN:
|
case PICT.OP_FRAME_RGN:
|
||||||
case PICT.OP_FRAME_SAME_RGN:
|
case PICT.OP_FRAME_SAME_RGN:
|
||||||
context.frameRegion(new Area(region));
|
context.frameRegion(region);
|
||||||
break;
|
break;
|
||||||
case PICT.OP_PAINT_RGN:
|
case PICT.OP_PAINT_RGN:
|
||||||
case PICT.OP_PAINT_SAME_RGN:
|
case PICT.OP_PAINT_SAME_RGN:
|
||||||
context.paintRegion(new Area(region));
|
context.paintRegion(region);
|
||||||
break;
|
break;
|
||||||
case PICT.OP_ERASE_RGN:
|
case PICT.OP_ERASE_RGN:
|
||||||
case PICT.OP_ERASE_SAME_RGN:
|
case PICT.OP_ERASE_SAME_RGN:
|
||||||
context.eraseRegion(new Area(region));
|
context.eraseRegion(region);
|
||||||
break;
|
break;
|
||||||
case PICT.OP_INVERT_RGN:
|
case PICT.OP_INVERT_RGN:
|
||||||
case PICT.OP_INVERT_SAME_RGN:
|
case PICT.OP_INVERT_SAME_RGN:
|
||||||
context.invertRegion(new Area(region));
|
context.invertRegion(region);
|
||||||
break;
|
break;
|
||||||
case PICT.OP_FILL_RGN:
|
case PICT.OP_FILL_RGN:
|
||||||
case PICT.OP_FILL_SAME_RGN:
|
case PICT.OP_FILL_SAME_RGN:
|
||||||
context.fillRegion(new Area(region), fill);
|
context.fillRegion(region, fill);
|
||||||
break;
|
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.
|
// 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
|
// [5] For opcodes $0090 (BitsRect) and $0091 (BitsRgn), the data is unpacked. These
|
||||||
// opcodes can be used only when rowBytes is less than 8.
|
// 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);
|
readOpBits(pStream, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PICT.OP_BITS_RGN:
|
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);
|
readOpBits(pStream, true);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1486,10 +1467,6 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
case PICT.OP_COMPRESSED_QUICKTIME:
|
case PICT.OP_COMPRESSED_QUICKTIME:
|
||||||
// $8200: CompressedQuickTime Data length (Long), data (private to QuickTime) 4 + data length
|
// $8200: CompressedQuickTime Data length (Long), data (private to QuickTime) 4 + data length
|
||||||
if (DEBUG) {
|
|
||||||
System.out.println("compressedQuickTime");
|
|
||||||
}
|
|
||||||
|
|
||||||
readCompressedQT(pStream);
|
readCompressedQT(pStream);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1585,43 +1562,56 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
int dataLength = pStream.readInt();
|
int dataLength = pStream.readInt();
|
||||||
long pos = pStream.getStreamPosition();
|
long pos = pStream.getStreamPosition();
|
||||||
|
|
||||||
if (DEBUG) {
|
int version = pStream.readUnsignedShort();
|
||||||
System.out.println("QT data length: " + dataLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Need to figure out what the skipped data is?
|
|
||||||
for (int i = 0; i < 13; i++) {
|
|
||||||
int value = pStream.readInt();
|
|
||||||
if (DEBUG) {
|
|
||||||
System.out.println(String.format("%2d: 0x%08x", i * 4, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the destination rectangle
|
|
||||||
Rectangle destination = new Rectangle();
|
|
||||||
readRectangle(pStream, destination);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println("...");
|
System.out.print("compressedQuickTime");
|
||||||
|
System.out.println(", size: " + dataLength + ", version: " + version);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
// Matrix
|
||||||
int value = pStream.readInt();
|
int[] matrix = new int[9];
|
||||||
if (DEBUG) {
|
for (int i = 0; i < matrix.length; i++) {
|
||||||
System.out.println(String.format("%2d: 0x%08x", (i + 15) * 4, value));
|
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);
|
BufferedImage image = QuickTime.decompress(pStream);
|
||||||
|
|
||||||
if (image != null) {
|
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
|
pStream.seek(pos + dataLength); // Might be word-align mismatch here
|
||||||
|
|
||||||
// Skip "QuickTime™ and a ... decompressor required" text
|
// Skip "QuickTime™ and a ... decompressor required" text
|
||||||
// TODO: Verify that this is correct. It works with all my test data, but the algorithm is
|
// NOTE: This algorithm is reverse-engineered by looking at various test inputs and not from any spec...
|
||||||
// reverse-engineered by looking at the input data and not from any spec I've seen...
|
|
||||||
int penSizeMagic = pStream.readInt();
|
int penSizeMagic = pStream.readInt();
|
||||||
if (penSizeMagic == 0x000700ae) { // OP_PN_SIZE + bogus x value..?
|
if (penSizeMagic == 0x000700ae) { // OP_PN_SIZE + bogus x value..?
|
||||||
int skip = pStream.readUnsignedShort(); // bogus y value is the number of bytes to skip
|
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();
|
Rectangle regionBounds = new Rectangle();
|
||||||
Polygon region = hasRegion ? readRegion(pStream, regionBounds) : null;
|
Area region = hasRegion ? readRegion(pStream, regionBounds) : null;
|
||||||
|
|
||||||
if (DEBUG) {
|
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
|
// Set up pixel buffer for the RGB values
|
||||||
@ -1882,8 +1877,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
BufferedImage img = images.get(pPixmapCount);
|
BufferedImage img = images.get(pPixmapCount);
|
||||||
if (img != null) {
|
if (img != null) {
|
||||||
srcRect.setLocation(0, 0); // Raster always start at 0,0
|
srcRect.setLocation(0, 0); // Raster always start at 0,0
|
||||||
// dstRect.translate(-frame.x, -frame.y);
|
context.copyBits(img, srcRect, dstRect, transferMode, region);
|
||||||
context.copyBits(img, srcRect, dstRect, transferMode, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Line break at the end
|
// 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
|
* @param pPixmapCount the index of the bitmap in the PICT file, used for
|
||||||
* cahcing.
|
* cahcing.
|
||||||
*
|
*
|
||||||
* @throws javax.imageio.IIOException if the data can not be read.
|
* @throws IIOException if the data can not be read.
|
||||||
* @throws java.io.IOException if an I/O error occurs while reading the image.
|
* @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 {
|
private void readOpDirectBits(final ImageInputStream pStream, final boolean hasRegion, final int pPixmapCount) throws IOException {
|
||||||
// Skip PixMap pointer (always 0x000000FF);
|
// Skip PixMap pointer (always 0x000000FF);
|
||||||
@ -2039,7 +2033,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
System.out.print(", mode: " + transferMode);
|
System.out.print(", mode: " + transferMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon region = hasRegion ? readRegion(pStream, new Rectangle()) : null;
|
Area region = hasRegion ? readRegion(pStream, new Rectangle()) : null;
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.println(hasRegion ? ", region: " + region : "");
|
System.out.println(hasRegion ? ", region: " + region : "");
|
||||||
@ -2196,8 +2190,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
BufferedImage img = images.get(pPixmapCount);
|
BufferedImage img = images.get(pPixmapCount);
|
||||||
if (img != null) {
|
if (img != null) {
|
||||||
srcRect.setLocation(0, 0); // Raster always starts at 0,0
|
srcRect.setLocation(0, 0); // Raster always starts at 0,0
|
||||||
// dstRect.translate(-frame.x, -frame.y);
|
context.copyBits(img, srcRect, dstRect, transferMode, region);
|
||||||
context.copyBits(img, srcRect, dstRect, transferMode, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Line break at the end
|
// 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 {
|
private void readOpBits(ImageInputStream pStream, boolean hasRegion) throws IOException {
|
||||||
// Get rowBytes
|
// Get rowBytes
|
||||||
int rowBytesRaw = pStream.readUnsignedShort();
|
int rowBytesRaw = pStream.readUnsignedShort();
|
||||||
@ -2323,7 +2326,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
// Get transfer mode
|
// Get transfer mode
|
||||||
int mode = pStream.readUnsignedShort();
|
int mode = pStream.readUnsignedShort();
|
||||||
|
|
||||||
Polygon region = hasRegion ? readRegion(pStream, new Rectangle()) : null;
|
Area region = hasRegion ? readRegion(pStream, new Rectangle()) : null;
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
System.out.print(", bounds: " + bounds);
|
System.out.print(", bounds: " + bounds);
|
||||||
@ -2347,8 +2350,7 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
// Draw pixel data
|
// Draw pixel data
|
||||||
srcRect.setLocation(0, 0); // Raster always start at 0,0
|
srcRect.setLocation(0, 0); // Raster always start at 0,0
|
||||||
// dstRect.translate(-frame.x, -frame.y);
|
context.copyBits(image, srcRect, dstRect, mode, region);
|
||||||
context.copyBits(image, srcRect, dstRect, mode, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2368,84 +2370,90 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
pDestRect.setLocation(getXPtCoord(x), getYPtCoord(y));
|
pDestRect.setLocation(getXPtCoord(x), getYPtCoord(y));
|
||||||
pDestRect.setSize(getXPtCoord(w - x), getYPtCoord(h - y));
|
pDestRect.setSize(getXPtCoord(w - x), getYPtCoord(h - y));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read in a region. The input stream should be positioned at the first byte
|
* 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.
|
* region bounds.
|
||||||
* The point array may therefore be empty if the region is just a rectangle.
|
* The point array may therefore be empty if the region is just a rectangle.
|
||||||
*
|
*
|
||||||
* @param pStream the stream to read from
|
* @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.
|
* region is a rectangle.
|
||||||
*
|
*
|
||||||
* @throws IOException if an I/O error occurs while reading the image.
|
* @throws IOException if an I/O error occurs while reading the image.
|
||||||
*/
|
*/
|
||||||
private Polygon readRegion(DataInput pStream, Rectangle pBoundsRect) throws IOException {
|
private Area readRegion(DataInput pStream, Rectangle pBounds) throws IOException {
|
||||||
// Get minimal region
|
|
||||||
|
|
||||||
// Get region data size
|
// Get region data size
|
||||||
int size = pStream.readUnsignedShort();
|
int size = pStream.readUnsignedShort();
|
||||||
|
|
||||||
// Get region bounds
|
// Get region bounds
|
||||||
int y = getYPtCoord(pStream.readShort());
|
readRectangle(pStream, pBounds);
|
||||||
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);
|
|
||||||
|
|
||||||
Polygon polygon = new Polygon();
|
|
||||||
int count = (size - 10) / 2;
|
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++) {
|
for (int i = 0; i < count; i++) {
|
||||||
short point = pStream.readShort();
|
short point = pStream.readShort();
|
||||||
|
|
||||||
if (nextIsV) {
|
if (nextIsVertical) {
|
||||||
if (point == 0x7fff) {
|
if (point == 0x7fff) {
|
||||||
// Done
|
// Done
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
y = point;
|
y = point;
|
||||||
nextIsV = false;
|
nextIsVertical = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (point == 0x7fff) {
|
if (point == 0x7fff) {
|
||||||
nextIsV = true;
|
nextIsVertical = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = point;
|
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
|
* Read in a polygon. The input stream should be positioned at the first byte
|
||||||
* of the polygon.
|
* 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
|
// Get polygon data size
|
||||||
int size = pStream.readUnsignedShort();
|
int size = pStream.readUnsignedShort();
|
||||||
|
|
||||||
// Get poly bounds
|
// Get poly bounds
|
||||||
int y = getYPtCoord(pStream.readShort());
|
readRectangle(pStream, pBounds);
|
||||||
int x = getXPtCoord(pStream.readShort());
|
|
||||||
pBoundsRect.setLocation(x, y);
|
|
||||||
|
|
||||||
y = getYPtCoord(pStream.readShort()) - pBoundsRect.getLocation().y;
|
|
||||||
x = getXPtCoord(pStream.readShort()) - pBoundsRect.getLocation().x;
|
|
||||||
pBoundsRect.setSize(x, y);
|
|
||||||
|
|
||||||
// Initialize the point array to the right size
|
// Initialize the point array to the right size
|
||||||
int points = (size - 10) / 4;
|
int points = (size - 10) / 4;
|
||||||
@ -2454,11 +2462,15 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
Polygon polygon = new Polygon();
|
Polygon polygon = new Polygon();
|
||||||
|
|
||||||
for (int i = 0; i < points; i++) {
|
for (int i = 0; i < points; i++) {
|
||||||
y = getYPtCoord(pStream.readShort());
|
int y = getYPtCoord(pStream.readShort());
|
||||||
x = getXPtCoord(pStream.readShort());
|
int x = getXPtCoord(pStream.readShort());
|
||||||
polygon.addPoint(x, y);
|
polygon.addPoint(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pBounds.contains(polygon.getBounds())) {
|
||||||
|
processWarningOccurred("Bad poly, contains point(s) out of bounds " + pBounds + ": " + polygon);
|
||||||
|
}
|
||||||
|
|
||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2540,15 +2552,15 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
/*
|
/*
|
||||||
* Write out region command, bounds and points.
|
* 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.println(pCmd + ": " + pBounds);
|
||||||
System.out.print("Region points: ");
|
// System.out.print("Region points: ");
|
||||||
if (pPolygon != null && pPolygon.npoints > 0) {
|
// if (pPolygon != null && pPolygon.npoints > 0) {
|
||||||
System.out.print("(" + pPolygon.xpoints[0] + "," + pPolygon.ypoints[0] + ")");
|
// System.out.print("(" + pPolygon.xpoints[0] + "," + pPolygon.ypoints[0] + ")");
|
||||||
}
|
// }
|
||||||
for (int i = 1; pPolygon != null && i < pPolygon.npoints; i++) {
|
// for (int i = 1; pPolygon != null && i < pPolygon.npoints; i++) {
|
||||||
System.out.print(", (" + pPolygon.xpoints[i] + "," + pPolygon.ypoints[i] + ")");
|
// System.out.print(", (" + pPolygon.xpoints[i] + "," + pPolygon.ypoints[i] + ")");
|
||||||
}
|
// }
|
||||||
System.out.println();
|
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;
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
import java.awt.image.*;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.image.DataBuffer;
|
||||||
|
import java.awt.image.IndexColorModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QuickDraw constants.
|
* 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-196.html#HEADING196-2
|
||||||
// http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-269.html#HEADING269-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!
|
// 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_COPY = 0;
|
||||||
int SRC_OR = 1;
|
int SRC_OR = 1;
|
||||||
int SRC_XOR = 2;
|
int SRC_XOR = 2;
|
||||||
@ -84,16 +97,73 @@ interface QuickDraw {
|
|||||||
|
|
||||||
// Arithmetic Transfer Modes
|
// Arithmetic Transfer Modes
|
||||||
// http://developer.apple.com/documentation/mac/quickdraw/QuickDraw-199.html#HEADING199-112
|
// 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 BLEND = 32; // dest = source weight/65,535 + destination (1 - weight/65,535)
|
||||||
int ADD_PIN = 33;
|
int ADD_PIN = 33;
|
||||||
int ADD_OVER = 34;
|
int ADD_OVER = 34;
|
||||||
int SUB_PIN = 35;
|
int SUB_PIN = 35;
|
||||||
int TRANSPARENT = 36;
|
int TRANSPARENT = 36;
|
||||||
int AD_MAX = 37;
|
int ADD_MAX = 37;
|
||||||
int SUB_OVER = 38;
|
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 GRAYISH_TEXT_OR = 49;
|
||||||
// int MASK = 64; // ?! From Käry's code..
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Text face masks.
|
* 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.*;
|
||||||
import java.awt.geom.*;
|
import java.awt.geom.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.ColorModel;
|
|
||||||
import java.awt.image.Raster;
|
import static java.lang.Math.sqrt;
|
||||||
import java.awt.image.WritableRaster;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulates an Apple QuickDraw rendering context, backed by a Java {@link Graphics2D}.
|
* Emulates an Apple QuickDraw rendering context, backed by a Java {@link Graphics2D}.
|
||||||
@ -308,9 +307,9 @@ class QuickDrawContext {
|
|||||||
case QuickDraw.ADD_OVER:
|
case QuickDraw.ADD_OVER:
|
||||||
case QuickDraw.SUB_PIN:
|
case QuickDraw.SUB_PIN:
|
||||||
case QuickDraw.TRANSPARENT:
|
case QuickDraw.TRANSPARENT:
|
||||||
case QuickDraw.AD_MAX:
|
case QuickDraw.ADD_MAX:
|
||||||
case QuickDraw.SUB_OVER:
|
case QuickDraw.SUB_OVER:
|
||||||
case QuickDraw.AD_MIN:
|
case QuickDraw.ADD_MIN:
|
||||||
case QuickDraw.GRAYISH_TEXT_OR:
|
case QuickDraw.GRAYISH_TEXT_OR:
|
||||||
penMode = pPenMode;
|
penMode = pPenMode;
|
||||||
break;
|
break;
|
||||||
@ -365,12 +364,11 @@ class QuickDrawContext {
|
|||||||
case QuickDraw.SRC_BIC:
|
case QuickDraw.SRC_BIC:
|
||||||
return AlphaComposite.Clear;
|
return AlphaComposite.Clear;
|
||||||
case QuickDraw.NOT_SRC_XOR:
|
case QuickDraw.NOT_SRC_XOR:
|
||||||
return new NotSrcXor();
|
return QuickDrawComposite.NotSrcXor;
|
||||||
case QuickDraw.NOT_SRC_COPY:
|
case QuickDraw.NOT_SRC_COPY:
|
||||||
case QuickDraw.NOT_SRC_OR:
|
case QuickDraw.NOT_SRC_OR:
|
||||||
case QuickDraw.NOT_SRC_BIC:
|
case QuickDraw.NOT_SRC_BIC:
|
||||||
throw new UnsupportedOperationException("Not implemented for mode " + pMode);
|
throw new UnsupportedOperationException("Not implemented for mode " + pMode);
|
||||||
// return null;
|
|
||||||
// Boolean pattern transfer modes
|
// Boolean pattern transfer modes
|
||||||
case QuickDraw.PAT_COPY:
|
case QuickDraw.PAT_COPY:
|
||||||
return AlphaComposite.Src; // Tested
|
return AlphaComposite.Src; // Tested
|
||||||
@ -385,8 +383,22 @@ class QuickDrawContext {
|
|||||||
case QuickDraw.NOT_PAT_XOR:
|
case QuickDraw.NOT_PAT_XOR:
|
||||||
case QuickDraw.NOT_PAT_BIC:
|
case QuickDraw.NOT_PAT_BIC:
|
||||||
throw new UnsupportedOperationException("Not implemented for mode " + pMode);
|
throw new UnsupportedOperationException("Not implemented for mode " + pMode);
|
||||||
// return null;
|
// Aritmetic transfer modes
|
||||||
// TODO: 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:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown pnMode: " + pMode);
|
throw new IllegalArgumentException("Unknown pnMode: " + pMode);
|
||||||
@ -401,7 +413,6 @@ class QuickDrawContext {
|
|||||||
graphics.setComposite(getCompositeFor(textMode));
|
graphics.setComposite(getCompositeFor(textMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up context for line drawing/painting.
|
* Sets up context for line drawing/painting.
|
||||||
*/
|
*/
|
||||||
@ -905,6 +916,7 @@ class QuickDrawContext {
|
|||||||
|
|
||||||
// Copying Images (SKIP?):
|
// Copying Images (SKIP?):
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CopyBits.
|
* CopyBits.
|
||||||
* <p/>
|
* <p/>
|
||||||
@ -941,14 +953,26 @@ class QuickDrawContext {
|
|||||||
/**
|
/**
|
||||||
* CopyMask
|
* 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
|
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
|
* 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
|
throw new UnsupportedOperationException("Method copyDeepMask not implemented"); // TODO: Implement
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1079,7 +1103,7 @@ class QuickDrawContext {
|
|||||||
thisY = points[1];
|
thisY = points[1];
|
||||||
float dx = thisX - lastX;
|
float dx = thisX - lastX;
|
||||||
float dy = thisY - lastY;
|
float dy = thisY - lastY;
|
||||||
float distance = (float) Math.sqrt(dx * dx + dy * dy);
|
float distance = (float) sqrt(dx * dx + dy * dy);
|
||||||
if (distance >= next) {
|
if (distance >= next) {
|
||||||
float r = 1.0f / distance;
|
float r = 1.0f / distance;
|
||||||
//float angle = (float) Math.atan2(dy, dx);
|
//float angle = (float) Math.atan2(dy, dx);
|
||||||
@ -1106,43 +1130,5 @@ class QuickDrawContext {
|
|||||||
|
|
||||||
return result;
|
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