mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 20:15:28 -04:00
#490: Refactorings, added initial detection of linked/unlinked segments, more tests.
This commit is contained in:
parent
f15bcc7df9
commit
51ace4ca7f
@ -28,9 +28,9 @@ public final class AdobePathBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see AdobePathReader#path()
|
||||
* @see AdobePathReader#readPath()
|
||||
*/
|
||||
public Path2D path() throws IOException {
|
||||
return delegate.path();
|
||||
return delegate.readPath();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* Copyright (c) 2014-2020, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -33,7 +33,6 @@ package com.twelvemonkeys.imageio.path;
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.io.DataInput;
|
||||
import java.io.EOFException;
|
||||
@ -90,7 +89,7 @@ public final class AdobePathReader {
|
||||
* @throws javax.imageio.IIOException if the input contains a bad path data.
|
||||
* @throws IOException if a general I/O exception occurs during reading.
|
||||
*/
|
||||
public Path2D path() throws IOException {
|
||||
public Path2D readPath() throws IOException {
|
||||
List<List<AdobePathSegment>> subPaths = new ArrayList<>();
|
||||
List<AdobePathSegment> currentPath = null;
|
||||
int currentPathLength = 0;
|
||||
@ -110,8 +109,8 @@ public final class AdobePathReader {
|
||||
subPaths.add(currentPath);
|
||||
}
|
||||
|
||||
currentPath = new ArrayList<>(segment.length);
|
||||
currentPathLength = segment.length;
|
||||
currentPath = new ArrayList<>(segment.lengthOrRule);
|
||||
currentPathLength = segment.lengthOrRule;
|
||||
}
|
||||
else if (segment.selector == AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED
|
||||
|| segment.selector == AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED
|
||||
@ -149,8 +148,8 @@ public final class AdobePathReader {
|
||||
* closePath()
|
||||
*/
|
||||
private Path2D pathToShape(final List<List<AdobePathSegment>> paths) {
|
||||
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD, paths.size());
|
||||
GeneralPath subpath = null;
|
||||
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD, paths.size());
|
||||
Path2D subpath = null;
|
||||
|
||||
for (List<AdobePathSegment> points : paths) {
|
||||
int length = points.size();
|
||||
@ -163,7 +162,7 @@ public final class AdobePathReader {
|
||||
switch (step) {
|
||||
// Begin
|
||||
case 0: {
|
||||
subpath = new GeneralPath(Path2D.WIND_EVEN_ODD, length);
|
||||
subpath = new Path2D.Float(Path2D.WIND_EVEN_ODD, length);
|
||||
subpath.moveTo(current.apx, current.apy);
|
||||
|
||||
if (length > 1) {
|
||||
@ -222,14 +221,12 @@ public final class AdobePathReader {
|
||||
switch (selector) {
|
||||
case AdobePathSegment.INITIAL_FILL_RULE_RECORD:
|
||||
case AdobePathSegment.PATH_FILL_RULE_RECORD:
|
||||
// Spec says Fill rule is ignored by Photoshop
|
||||
data.skipBytes(24);
|
||||
return new AdobePathSegment(selector);
|
||||
// Spec says Fill rule is ignored by Photoshop, we'll read it anyway
|
||||
case AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD:
|
||||
case AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD:
|
||||
int size = data.readUnsignedShort();
|
||||
int lengthOrRule = data.readUnsignedShort();
|
||||
data.skipBytes(22);
|
||||
return new AdobePathSegment(selector, size);
|
||||
return new AdobePathSegment(selector, lengthOrRule);
|
||||
default:
|
||||
return new AdobePathSegment(
|
||||
selector,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* Copyright (c) 2014-2020, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -63,7 +63,7 @@ final class AdobePathSegment {
|
||||
};
|
||||
|
||||
final int selector;
|
||||
final int length;
|
||||
final int lengthOrRule;
|
||||
|
||||
// TODO: Consider keeping these in 8.24FP format
|
||||
// Control point preceding knot
|
||||
@ -85,18 +85,14 @@ final class AdobePathSegment {
|
||||
this(selector, -1, cppy, cppx, apy, apx, cply, cplx);
|
||||
}
|
||||
|
||||
AdobePathSegment(int fillRuleSelector) {
|
||||
this(isTrue(fillRuleSelector == PATH_FILL_RULE_RECORD || fillRuleSelector == INITIAL_FILL_RULE_RECORD, fillRuleSelector, "Expected fill rule record (6 or 8): %s"),
|
||||
0, -1, -1, -1, -1, -1, -1);
|
||||
}
|
||||
|
||||
AdobePathSegment(final int lengthSelector, final int length) {
|
||||
this(isTrue(lengthSelector == CLOSED_SUBPATH_LENGTH_RECORD || lengthSelector == OPEN_SUBPATH_LENGTH_RECORD, lengthSelector, "Expected path length record (0 or 3): %s"),
|
||||
length,
|
||||
AdobePathSegment(final int selector, final int lengthOrRule) {
|
||||
this(isTrue(selector == CLOSED_SUBPATH_LENGTH_RECORD || selector == OPEN_SUBPATH_LENGTH_RECORD
|
||||
|| selector == PATH_FILL_RULE_RECORD || selector == INITIAL_FILL_RULE_RECORD, selector, "Expected path length or fill rule record (0/3 or 6/8): %s"),
|
||||
lengthOrRule,
|
||||
-1, -1, -1, -1, -1, -1);
|
||||
}
|
||||
|
||||
private AdobePathSegment(final int selector, final int length,
|
||||
private AdobePathSegment(final int selector, final int lengthOrRule,
|
||||
final double cppy, final double cppx,
|
||||
final double apy, final double apx,
|
||||
final double cply, final double cplx) {
|
||||
@ -104,7 +100,7 @@ final class AdobePathSegment {
|
||||
switch (selector) {
|
||||
case CLOSED_SUBPATH_LENGTH_RECORD:
|
||||
case OPEN_SUBPATH_LENGTH_RECORD:
|
||||
isTrue(length >= 0, length, "Expected positive length: %d");
|
||||
isTrue(lengthOrRule >= 0, lengthOrRule, "Expected positive length: %d");
|
||||
break;
|
||||
case CLOSED_SUBPATH_BEZIER_LINKED:
|
||||
case CLOSED_SUBPATH_BEZIER_UNLINKED:
|
||||
@ -116,15 +112,17 @@ final class AdobePathSegment {
|
||||
);
|
||||
break;
|
||||
case PATH_FILL_RULE_RECORD:
|
||||
case CLIPBOARD_RECORD:
|
||||
case INITIAL_FILL_RULE_RECORD:
|
||||
isTrue(lengthOrRule == 0 || lengthOrRule == 1, lengthOrRule, "Expected rule (1 or 0): %d");
|
||||
break;
|
||||
case CLIPBOARD_RECORD:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown selector: " + selector);
|
||||
}
|
||||
|
||||
this.selector = selector;
|
||||
this.length = length;
|
||||
this.lengthOrRule = lengthOrRule;
|
||||
this.cppy = cppy;
|
||||
this.cppx = cppx;
|
||||
this.apy = apy;
|
||||
@ -152,7 +150,7 @@ final class AdobePathSegment {
|
||||
&& Double.compare(that.cppx, cppx) == 0
|
||||
&& Double.compare(that.cppy, cppy) == 0
|
||||
&& selector == that.selector
|
||||
&& length == that.length;
|
||||
&& lengthOrRule == that.lengthOrRule;
|
||||
|
||||
}
|
||||
|
||||
@ -161,7 +159,7 @@ final class AdobePathSegment {
|
||||
long tempBits;
|
||||
|
||||
int result = selector;
|
||||
result = 31 * result + length;
|
||||
result = 31 * result + lengthOrRule;
|
||||
tempBits = Double.doubleToLongBits(cppy);
|
||||
result = 31 * result + (int) (tempBits ^ (tempBits >>> 32));
|
||||
tempBits = Double.doubleToLongBits(cppx);
|
||||
@ -183,10 +181,10 @@ final class AdobePathSegment {
|
||||
switch (selector) {
|
||||
case INITIAL_FILL_RULE_RECORD:
|
||||
case PATH_FILL_RULE_RECORD:
|
||||
return String.format("Rule(selector=%s, rule=%d)", SELECTOR_NAMES[selector], length);
|
||||
return String.format("Rule(selector=%s, rule=%d)", SELECTOR_NAMES[selector], lengthOrRule);
|
||||
case CLOSED_SUBPATH_LENGTH_RECORD:
|
||||
case OPEN_SUBPATH_LENGTH_RECORD:
|
||||
return String.format("Len(selector=%s, length=%d)", SELECTOR_NAMES[selector], length);
|
||||
return String.format("Len(selector=%s, length=%d)", SELECTOR_NAMES[selector], lengthOrRule);
|
||||
default:
|
||||
// fall-through
|
||||
}
|
||||
|
@ -1,3 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.path;
|
||||
|
||||
import com.twelvemonkeys.imageio.metadata.psd.PSD;
|
||||
@ -13,6 +43,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.path.AdobePathReader.DEBUG;
|
||||
import static com.twelvemonkeys.imageio.path.AdobePathSegment.*;
|
||||
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
@ -27,10 +58,10 @@ public final class AdobePathWriter {
|
||||
/**
|
||||
* Creates an AdobePathWriter for the given path.
|
||||
* <p>
|
||||
* NOTE: Photoshop paths are stored with the coordinates
|
||||
* (0,0) representing the top left corner of the image,
|
||||
* and (1,1) representing the bottom right corner,
|
||||
* regardless of image dimensions.
|
||||
* NOTE: Photoshop paths are stored with the coordinates
|
||||
* (0,0) representing the top left corner of the image,
|
||||
* and (1,1) representing the bottom right corner,
|
||||
* regardless of image dimensions.
|
||||
* </p>
|
||||
*
|
||||
* @param path A {@code Path2D} instance that has {@link Path2D#WIND_EVEN_ODD WIND_EVEN_ODD} rule
|
||||
@ -51,16 +82,24 @@ public final class AdobePathWriter {
|
||||
// TODO: Look at the API so that conversion both ways are aligned. The read part builds a path from List<List<AdobePathSegment>...
|
||||
private static List<AdobePathSegment> pathToSegments(final PathIterator pathIterator) {
|
||||
double[] coords = new double[6];
|
||||
AdobePathSegment prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, 0, 0, 0,0, 0, 0);
|
||||
AdobePathSegment prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
List<AdobePathSegment> subpath = new ArrayList<>();
|
||||
List<AdobePathSegment> segments = new ArrayList<>();
|
||||
segments.add(new AdobePathSegment(PATH_FILL_RULE_RECORD));
|
||||
segments.add(new AdobePathSegment(PATH_FILL_RULE_RECORD, 0));
|
||||
segments.add(new AdobePathSegment(INITIAL_FILL_RULE_RECORD, 0));
|
||||
|
||||
while (!pathIterator.isDone()) {
|
||||
int segmentType = pathIterator.currentSegment(coords);
|
||||
System.out.println("segmentType: " + segmentType);
|
||||
System.err.println("coords: " + Arrays.toString(coords));
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("segmentType: " + segmentType);
|
||||
System.err.println("coords: " + Arrays.toString(coords));
|
||||
}
|
||||
|
||||
// TODO: We need to support unlinked segments!
|
||||
|
||||
boolean collinear;
|
||||
|
||||
switch (segmentType) {
|
||||
case PathIterator.SEG_MOVETO:
|
||||
@ -71,17 +110,23 @@ public final class AdobePathWriter {
|
||||
break;
|
||||
|
||||
case PathIterator.SEG_LINETO:
|
||||
subpath.add(new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
|
||||
System.out.println("isCollinear? " + collinear);
|
||||
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[1], coords[0], coords[1], coords[0], 0, 0);
|
||||
break;
|
||||
|
||||
case PathIterator.SEG_QUADTO:
|
||||
subpath.add(new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
|
||||
System.out.println("isCollinear? " + collinear);
|
||||
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[3], coords[2], coords[3], coords[2], 0, 0);
|
||||
break;
|
||||
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
subpath.add(new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, prev.apx, prev.apy, coords[0], coords[1]);
|
||||
System.out.println("isCollinear? " + collinear);
|
||||
subpath.add(new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, coords[1], coords[0]));
|
||||
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, coords[3], coords[2], coords[5], coords[4], 0, 0);
|
||||
break;
|
||||
|
||||
@ -93,7 +138,10 @@ public final class AdobePathWriter {
|
||||
// subpath.add(new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, prev.cppy, prev.cppx, prev.apy, prev.apx, 0, 0));
|
||||
throw new AssertionError("Not a closed path");
|
||||
}
|
||||
subpath.set(0, new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, prev.cppy, prev.cppx, initial.apy, initial.apx, initial.cply, initial.cplx));
|
||||
|
||||
collinear = isCollinearAndSameDistance(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.cplx, initial.cply);
|
||||
System.out.println("isCollinear? " + collinear);
|
||||
subpath.set(0, new AdobePathSegment(collinear ? CLOSED_SUBPATH_BEZIER_LINKED : CLOSED_SUBPATH_BEZIER_UNLINKED, prev.cppy, prev.cppx, initial.apy, initial.apx, initial.cply, initial.cplx));
|
||||
|
||||
// Add to full path
|
||||
segments.add(new AdobePathSegment(CLOSED_SUBPATH_LENGTH_RECORD, subpath.size()));
|
||||
@ -110,17 +158,38 @@ public final class AdobePathWriter {
|
||||
return segments;
|
||||
}
|
||||
|
||||
public void writePath(final DataOutput output) throws IOException {
|
||||
System.err.println("segments: " + segments.size());
|
||||
private static final double COLLINEARITY_THRESHOLD = 0.035;
|
||||
|
||||
private static boolean isCollinearAndSameDistance(double x1, double y1, double x2, double y2, double x3, double y3) {
|
||||
// return (y3 - y2) * (x2 - x1) == (y2 - y1) * (x3 - x2); // Collinear Slope
|
||||
// return x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2) == 0; // Collinear (Double) Area
|
||||
|
||||
// return Math.abs(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) <= 0.0005; // With some slack...
|
||||
|
||||
// return Math.abs(Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)) - Math.sqrt(Math.pow(x3 - x2, 2) + Math.pow(y3 - y2, 2))) <= 0.01;
|
||||
|
||||
// TODO: Get hold of a real Photoshop sample... The current data may be wrong.
|
||||
// TODO: If correct, PS writes linked if all points are the same...
|
||||
return (x1 == x2 && x2 == x3 && y1 == y2 && y2 == y3) ||
|
||||
(x1 != x2 || y1 != y2) && (x2 != x3 || y2 != y3) && Math.abs((x2 - x1) - (x3 - x2)) <= COLLINEARITY_THRESHOLD && Math.abs((y2 - y1) - (y3 - y2)) <= COLLINEARITY_THRESHOLD;
|
||||
}
|
||||
|
||||
void writePathResource(final DataOutput output) throws IOException {
|
||||
output.writeInt(PSD.RESOURCE_TYPE);
|
||||
output.writeShort(PSD.RES_CLIPPING_PATH);
|
||||
output.writeShort(0); // Path name (Pascal string) empty + pad
|
||||
output.writeInt(segments.size() * 26); // Resource size
|
||||
|
||||
writePath(output);
|
||||
}
|
||||
|
||||
public void writePath(final DataOutput output) throws IOException {
|
||||
if (DEBUG) {
|
||||
System.err.println("segments: " + segments.size());
|
||||
System.err.println(segments);
|
||||
}
|
||||
|
||||
for (AdobePathSegment segment : segments) {
|
||||
System.err.println(segment);
|
||||
switch (segment.selector) {
|
||||
case PATH_FILL_RULE_RECORD:
|
||||
case INITIAL_FILL_RULE_RECORD:
|
||||
@ -132,7 +201,7 @@ public final class AdobePathWriter {
|
||||
case OPEN_SUBPATH_LENGTH_RECORD:
|
||||
case CLOSED_SUBPATH_LENGTH_RECORD:
|
||||
output.writeShort(segment.selector);
|
||||
output.writeShort(segment.length); // Subpath length
|
||||
output.writeShort(segment.lengthOrRule); // Subpath length
|
||||
output.write(new byte[22]);
|
||||
break;
|
||||
default:
|
||||
@ -148,13 +217,15 @@ public final class AdobePathWriter {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] createPath() {
|
||||
// TODO: Better name?
|
||||
public byte[] writePath() {
|
||||
// TODO: Do we need to care about endianness for TIFF files?
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
|
||||
try (DataOutputStream stream = new DataOutputStream(bytes)) {
|
||||
writePath(stream);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError("Should never.. uh.. Oh well. It happened.", e);
|
||||
}
|
||||
|
||||
|
@ -163,10 +163,10 @@ public final class Paths {
|
||||
System.out.println("resourceBlocks: " + resourceBlocks);
|
||||
}
|
||||
|
||||
Entry resourceBlock = resourceBlocks.getEntryById(PSD.RES_CLIPPING_PATH);
|
||||
Entry pathResource = resourceBlocks.getEntryById(PSD.RES_CLIPPING_PATH);
|
||||
|
||||
if (resourceBlock != null) {
|
||||
return new AdobePathReader((byte[]) resourceBlock.getValue()).path();
|
||||
if (pathResource != null) {
|
||||
return new AdobePathReader((byte[]) pathResource.getValue()).readPath();
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -259,9 +259,7 @@ public final class Paths {
|
||||
BufferedImage destination;
|
||||
if (args.length == 1) {
|
||||
// Embedded path
|
||||
try (ImageInputStream input = ImageIO.createImageInputStream(new File(args[0]))) {
|
||||
destination = readClipped(input);
|
||||
}
|
||||
destination = readClipped(ImageIO.createImageInputStream(new File(args[0])));
|
||||
}
|
||||
else {
|
||||
// Separate path and image
|
||||
@ -282,5 +280,4 @@ public final class Paths {
|
||||
System.err.printf("%s not deleted\n", tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals;
|
||||
import static com.twelvemonkeys.imageio.path.PathsTest.readExpectedPath;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AdobePathBuilderTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.path;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals;
|
||||
import static com.twelvemonkeys.imageio.path.PathsTest.readExpectedPath;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class AdobePathReaderTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateNullBytes() {
|
||||
new AdobePathReader((byte[]) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateNull() {
|
||||
new AdobePathReader((DataInput) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateEmpty() {
|
||||
new AdobePathReader(new byte[0]);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateShortPath() {
|
||||
new AdobePathReader(new byte[3]);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateImpossiblePath() {
|
||||
new AdobePathReader(new byte[7]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
new AdobePathReader(new byte[52]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoPath() throws IOException {
|
||||
Path2D path = new AdobePathReader(new byte[26]).readPath();
|
||||
assertNotNull(path);
|
||||
}
|
||||
|
||||
@Test(expected = IIOException.class)
|
||||
public void testShortPath() throws IOException {
|
||||
byte[] data = new byte[26];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD);
|
||||
buffer.putShort((short) 1);
|
||||
|
||||
Path2D path = new AdobePathReader(data).readPath();
|
||||
assertNotNull(path);
|
||||
}
|
||||
|
||||
@Test(expected = IIOException.class)
|
||||
public void testShortPathToo() throws IOException {
|
||||
byte[] data = new byte[52];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD);
|
||||
buffer.putShort((short) 2);
|
||||
buffer.position(buffer.position() + 22);
|
||||
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED);
|
||||
|
||||
Path2D path = new AdobePathReader(data).readPath();
|
||||
assertNotNull(path);
|
||||
}
|
||||
|
||||
@Test(expected = IIOException.class)
|
||||
public void testLongPath() throws IOException {
|
||||
byte[] data = new byte[78];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD);
|
||||
buffer.putShort((short) 1);
|
||||
buffer.position(buffer.position() + 22);
|
||||
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED);
|
||||
buffer.position(buffer.position() + 24);
|
||||
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED);
|
||||
|
||||
Path2D path = new AdobePathReader(data).readPath();
|
||||
assertNotNull(path);
|
||||
}
|
||||
|
||||
@Test(expected = IIOException.class)
|
||||
public void testPathMissingLength() throws IOException {
|
||||
byte[] data = new byte[26];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(data);
|
||||
buffer.putShort((short) AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED);
|
||||
|
||||
Path2D path = new AdobePathReader(data).readPath();
|
||||
assertNotNull(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimplePath() throws IOException {
|
||||
// We'll read this from a real file, with hardcoded offsets for simplicity
|
||||
// PSD IRB: offset: 34, length: 32598
|
||||
// Clipping path: offset: 31146, length: 1248
|
||||
ImageInputStream stream = PathsTest.resourceAsIIOStream("/psd/grape_with_path.psd");
|
||||
stream.seek(34 + 31146);
|
||||
byte[] data = new byte[1248];
|
||||
stream.readFully(data);
|
||||
|
||||
Path2D path = new AdobePathReader(data).readPath();
|
||||
|
||||
assertNotNull(path);
|
||||
assertPathEquals(path, readExpectedPath("/ser/grape-path.ser"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplexPath() throws IOException {
|
||||
// We'll read this from a real file, with hardcoded offsets for simplicity
|
||||
// PSD IRB: offset: 16970, length: 11152
|
||||
// Clipping path: offset: 9250, length: 1534
|
||||
ImageInputStream stream = PathsTest.resourceAsIIOStream("/tiff/big-endian-multiple-clips.tif");
|
||||
stream.seek(16970 + 9250);
|
||||
byte[] data = new byte[1534];
|
||||
stream.readFully(data);
|
||||
|
||||
Path2D path = new AdobePathReader(data).readPath();
|
||||
|
||||
assertNotNull(path);
|
||||
assertPathEquals(path, readExpectedPath("/ser/multiple-clips.ser"));
|
||||
}
|
||||
}
|
@ -63,7 +63,7 @@ public class AdobePathSegmentTest {
|
||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, 42);
|
||||
|
||||
assertEquals(AdobePathSegment.OPEN_SUBPATH_LENGTH_RECORD, segment.selector);
|
||||
assertEquals(42, segment.length);
|
||||
assertEquals(42, segment.lengthOrRule);
|
||||
assertEquals(-1, segment.cppx, 0);
|
||||
assertEquals(-1, segment.cppy, 0);
|
||||
assertEquals(-1, segment.apx, 0);
|
||||
@ -82,7 +82,7 @@ public class AdobePathSegmentTest {
|
||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD, 27);
|
||||
|
||||
assertEquals(AdobePathSegment.CLOSED_SUBPATH_LENGTH_RECORD, segment.selector);
|
||||
assertEquals(27, segment.length);
|
||||
assertEquals(27, segment.lengthOrRule);
|
||||
assertEquals(-1, segment.cppx, 0);
|
||||
assertEquals(-1, segment.cppy, 0);
|
||||
assertEquals(-1, segment.apx, 0);
|
||||
@ -98,7 +98,7 @@ public class AdobePathSegmentTest {
|
||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, .5, .5, 0, 0, 1, 1);
|
||||
|
||||
assertEquals(AdobePathSegment.OPEN_SUBPATH_BEZIER_LINKED, segment.selector);
|
||||
assertEquals(-1, segment.length);
|
||||
assertEquals(-1, segment.lengthOrRule);
|
||||
assertEquals(.5, segment.cppx, 0);
|
||||
assertEquals(.5, segment.cppy, 0);
|
||||
assertEquals(0, segment.apx, 0);
|
||||
@ -122,7 +122,7 @@ public class AdobePathSegmentTest {
|
||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, .5, .5, 0, 0, 1, 1);
|
||||
|
||||
assertEquals(AdobePathSegment.OPEN_SUBPATH_BEZIER_UNLINKED, segment.selector);
|
||||
assertEquals(-1, segment.length);
|
||||
assertEquals(-1, segment.lengthOrRule);
|
||||
assertEquals(.5, segment.cppx, 0);
|
||||
assertEquals(.5, segment.cppy, 0);
|
||||
assertEquals(0, segment.apx, 0);
|
||||
@ -149,7 +149,7 @@ public class AdobePathSegmentTest {
|
||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, .5, .5, 0, 0, 1, 1);
|
||||
|
||||
assertEquals(AdobePathSegment.CLOSED_SUBPATH_BEZIER_LINKED, segment.selector);
|
||||
assertEquals(-1, segment.length);
|
||||
assertEquals(-1, segment.lengthOrRule);
|
||||
assertEquals(.5, segment.cppx, 0);
|
||||
assertEquals(.5, segment.cppy, 0);
|
||||
assertEquals(0, segment.apx, 0);
|
||||
@ -173,7 +173,7 @@ public class AdobePathSegmentTest {
|
||||
AdobePathSegment segment = new AdobePathSegment(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, .5, .5, 0, 0, 1, 1);
|
||||
|
||||
assertEquals(AdobePathSegment.CLOSED_SUBPATH_BEZIER_UNLINKED, segment.selector);
|
||||
assertEquals(-1, segment.length);
|
||||
assertEquals(-1, segment.lengthOrRule);
|
||||
assertEquals(.5, segment.cppx, 0);
|
||||
assertEquals(.5, segment.cppy, 0);
|
||||
assertEquals(0, segment.apx, 0);
|
||||
@ -195,7 +195,7 @@ public class AdobePathSegmentTest {
|
||||
|
||||
@Test
|
||||
public void testToStringRule() {
|
||||
String string = new AdobePathSegment(AdobePathSegment.INITIAL_FILL_RULE_RECORD).toString();
|
||||
String string = new AdobePathSegment(AdobePathSegment.INITIAL_FILL_RULE_RECORD, 0).toString();
|
||||
assertTrue(string, string.startsWith("Rule"));
|
||||
assertTrue(string, string.contains("Initial"));
|
||||
assertTrue(string, string.contains("fill"));
|
||||
|
@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder 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 HOLDER 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.path;
|
||||
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.twelvemonkeys.imageio.path.AdobePathSegment.*;
|
||||
import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* AdobePathWriterTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by haraldk: harald.kuhr$
|
||||
* @version : AdobePathWriterTest.java,v 1.0 2020-01-02 harald.kuhr Exp$
|
||||
*/
|
||||
public class AdobePathWriterTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateWriterNull() {
|
||||
new AdobePathWriter(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateWriterInvalid() {
|
||||
new AdobePathWriter(new Path2D.Double(Path2D.WIND_NON_ZERO));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testCreateWriterOutOfBounds() {
|
||||
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
|
||||
path.append(new Ellipse2D.Double(.5, 0.5, 2, 2), false);
|
||||
|
||||
new AdobePathWriter(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWriterValid() {
|
||||
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
|
||||
path.append(new Ellipse2D.Double(.25, .25, .5, .5), false);
|
||||
|
||||
new AdobePathWriter(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWriterMulti() {
|
||||
Path2D path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||
path.append(new Ellipse2D.Float(.25f, .25f, .5f, .5f), false);
|
||||
path.append(new Rectangle2D.Double(0, 0, 1, .5), false);
|
||||
path.append(new Polygon(new int[] {1, 2, 0, 1}, new int[] {0, 2, 2, 0}, 4)
|
||||
.getPathIterator(AffineTransform.getScaleInstance(1 / 2.0, 1 / 2.0)), false);
|
||||
|
||||
new AdobePathWriter(path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteToStream() throws IOException {
|
||||
Path2D path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||
path.append(new Ellipse2D.Double(0, 0, 1, 1), false);
|
||||
path.append(new Ellipse2D.Double(.5, .5, .5, .5), false);
|
||||
path.append(new Ellipse2D.Double(.25, .25, .5, .5), false);
|
||||
|
||||
AdobePathWriter pathCreator = new AdobePathWriter(path);
|
||||
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
try (ImageOutputStream output = ImageIO.createImageOutputStream(byteStream)) {
|
||||
pathCreator.writePath(output);
|
||||
}
|
||||
|
||||
assertEquals(17 * 26, byteStream.size());
|
||||
|
||||
byte[] bytes = byteStream.toByteArray();
|
||||
|
||||
int off = 0;
|
||||
|
||||
// Path/initial fill rule: Even-Odd (0)
|
||||
assertArrayEquals(new byte[] {0, PATH_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, INITIAL_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
|
||||
// Elipse 1: 0, 0, 1, 1
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, 57, 78, -68, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 0, -58, -79, 68, 1, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 1, 0, 0, 0, 0, -58, -79, 68, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 0, 57, 78, -68},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -58, -79, 68, 0, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0, 0, 57, 78, -68, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, 0, 0, 0, 0, 57, 78, -68, 0, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0, 0, -58, -79, 68},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
|
||||
// Elipse 2: .5, .5, .5, .5
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -100, -89, 94, 1, 0, 0, 0, 0, -64, 0, 0, 1, 0, 0, 0, 0, -29, 88, -94, 1, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 1, 0, 0, 0, 0, -29, 88, -94, 1, 0, 0, 0, 0, -64, 0, 0, 1, 0, 0, 0, 0, -100, -89, 94},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -29, 88, -94, 0, -128, 0, 0, 0, -64, 0, 0, 0, -128, 0, 0, 0, -100, -89, 94, 0, -128, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -128, 0, 0, 0, -100, -89, 94, 0, -128, 0, 0, 0, -64, 0, 0, 0, -128, 0, 0, 0, -29, 88, -94},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
|
||||
// Elipse32: .25, .25, .5, .5
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, 92, -89, 94, 0, -64, 0, 0, 0, -128, 0, 0, 0, -64, 0, 0, 0, -93, 88, -94, 0, -64, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -64, 0, 0, 0, -93, 88, -94, 0, -64, 0, 0, 0, -128, 0, 0, 0, -64, 0, 0, 0, 92, -89, 94},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, -93, 88, -94, 0, 64, 0, 0, 0, -128, 0, 0, 0, 64, 0, 0, 0, 92, -89, 94, 0, 64, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_LINKED, 0, 64, 0, 0, 0, 92, -89, 94, 0, 64, 0, 0, 0, -128, 0, 0, 0, 64, 0, 0, 0, -93, 88, -94},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
|
||||
// Sanity
|
||||
assertEquals(bytes.length, off);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateArray() {
|
||||
Path2D path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||
path.append(new Rectangle2D.Double(0, 0, 1, .5), false);
|
||||
path.append(new Rectangle2D.Double(.25, .25, .5, .5), false);
|
||||
|
||||
AdobePathWriter pathCreator = new AdobePathWriter(path);
|
||||
|
||||
byte[] bytes = pathCreator.writePath();
|
||||
System.err.println(Arrays.toString(bytes));
|
||||
|
||||
assertEquals(12 * 26, bytes.length);
|
||||
|
||||
int off = 0;
|
||||
|
||||
// Path/initial fill rule: Even-Odd (0)
|
||||
assertArrayEquals(new byte[] {0, PATH_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, INITIAL_FILL_RULE_RECORD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
|
||||
// Rectangle 1: 0, 0, 1, .5
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -128, 0, 0, 0, 0, 0, 0, 0, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
|
||||
// Rectangle 2: .25, .25, .5, .5
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_LENGTH_RECORD, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, 64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0, 0, -64, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, 64, 0, 0, 0, -64, 0, 0, 0, 64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0, 0, -64, 0, 0, 0, 64, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 0, -64, 0, 0, 0, 64, 0, 0, 0, -64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0, 0, 64, 0, 0},
|
||||
Arrays.copyOfRange(bytes, off, off += 26));
|
||||
|
||||
// Sanity
|
||||
assertEquals(bytes.length, off);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundtrip0() throws IOException {
|
||||
Path2D path = new GeneralPath(Path2D.WIND_EVEN_ODD);
|
||||
path.append(new Rectangle2D.Double(0, 0, 1, .5), false);
|
||||
path.append(new Rectangle2D.Double(.25, .25, .5, .5), false);
|
||||
|
||||
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||
Path2D readPath = new AdobePathReader(new ByteArrayImageInputStream(bytes)).readPath();
|
||||
|
||||
assertEquals(path.getWindingRule(), readPath.getWindingRule());
|
||||
assertEquals(path.getBounds2D(), readPath.getBounds2D());
|
||||
|
||||
// TODO: Would be nice, but hard to do, as we convert all points to cubic...
|
||||
// assertPathEquals(path, readPath);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundtrip1() throws IOException {
|
||||
// We'll read this from a real file, with hardcoded offsets for simplicity
|
||||
// PSD IRB: offset: 34, length: 32598
|
||||
// Clipping path: offset: 31146, length: 1248
|
||||
ImageInputStream stream = PathsTest.resourceAsIIOStream("/psd/grape_with_path.psd");
|
||||
stream.seek(34 + 31146);
|
||||
byte[] data = new byte[1248];
|
||||
stream.readFully(data);
|
||||
|
||||
Path2D path = new AdobePathReader(data).readPath();
|
||||
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||
|
||||
Path2D readPath = new AdobePathReader(new ByteArrayImageInputStream(bytes)).readPath();
|
||||
assertEquals(path.getWindingRule(), readPath.getWindingRule());
|
||||
assertEquals(path.getBounds2D(), readPath.getBounds2D());
|
||||
|
||||
assertPathEquals(path, readPath);
|
||||
|
||||
assertEquals(data.length, bytes.length);
|
||||
|
||||
// TODO: We currently write all points as linked, this is probably wrong
|
||||
// Also... Photoshop does write "something" undocumented in the filler bytes for the length records, which may or may not be important...
|
||||
// assertEquals(formatSegments(data), formatSegments(bytes));
|
||||
// assertArrayEquals(data, bytes);
|
||||
}
|
||||
|
||||
static String formatSegments(byte[] data) {
|
||||
StringBuilder builder = new StringBuilder(data.length * 5);
|
||||
|
||||
for (int i = 0; i < data.length; i += 26) {
|
||||
builder.append(Arrays.toString(Arrays.copyOfRange(data, i, i + 26))).append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundtrip2() throws IOException {
|
||||
// We'll read this from a real file, with hardcoded offsets for simplicity
|
||||
// PSD IRB: offset: 16970, length: 11152
|
||||
// Clipping path: offset: 9250, length: 1534
|
||||
ImageInputStream stream = PathsTest.resourceAsIIOStream("/tiff/big-endian-multiple-clips.tif");
|
||||
stream.seek(16970 + 9250);
|
||||
byte[] data = new byte[1534];
|
||||
stream.readFully(data);
|
||||
|
||||
Path2D path = new AdobePathReader(data).readPath();
|
||||
byte[] bytes = new AdobePathWriter(path).writePath();
|
||||
|
||||
Path2D readPath = new AdobePathReader(new ByteArrayImageInputStream(bytes)).readPath();
|
||||
assertEquals(path.getWindingRule(), readPath.getWindingRule());
|
||||
assertEquals(path.getBounds2D(), readPath.getBounds2D());
|
||||
|
||||
assertPathEquals(path, readPath);
|
||||
|
||||
assertEquals(data.length, bytes.length);
|
||||
|
||||
// TODO: We currently write all points as linked, this is probably wrong
|
||||
// Also... Photoshop does write "something" undocumented in the filler bytes for the length records, which may or may not be important...
|
||||
// assertEquals(formatSegments(data), formatSegments(bytes));
|
||||
// assertArrayEquals(data, bytes);
|
||||
}
|
||||
}
|
@ -125,12 +125,12 @@ public class PathsTest {
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testApplyClippingPathNullPath() throws IOException {
|
||||
public void testApplyClippingPathNullPath() {
|
||||
Paths.applyClippingPath(null, new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testApplyClippingPathNullSource() throws IOException {
|
||||
public void testApplyClippingPathNullSource() {
|
||||
Paths.applyClippingPath(new GeneralPath(), null);
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ public class PathsTest {
|
||||
assertEquals(source.getWidth(), image.getWidth());
|
||||
assertEquals(source.getHeight(), image.getHeight());
|
||||
// Transparent
|
||||
assertTrue(image.getColorModel().getTransparency() == Transparency.TRANSLUCENT);
|
||||
assertEquals(Transparency.TRANSLUCENT, image.getColorModel().getTransparency());
|
||||
|
||||
// Corners (at least) should be transparent
|
||||
assertEquals(0, image.getRGB(0, 0));
|
||||
@ -161,8 +161,9 @@ public class PathsTest {
|
||||
// TODO: Mor sophisticated test that tests all pixels outside path...
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testApplyClippingPathNullDestination() throws IOException {
|
||||
public void testApplyClippingPathNullDestination() {
|
||||
Paths.applyClippingPath(new GeneralPath(), new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), null);
|
||||
}
|
||||
|
||||
@ -209,7 +210,7 @@ public class PathsTest {
|
||||
assertEquals(857, image.getWidth());
|
||||
assertEquals(1800, image.getHeight());
|
||||
// Transparent
|
||||
assertTrue(image.getColorModel().getTransparency() == Transparency.TRANSLUCENT);
|
||||
assertEquals(Transparency.TRANSLUCENT, image.getColorModel().getTransparency());
|
||||
|
||||
// Corners (at least) should be transparent
|
||||
assertEquals(0, image.getRGB(0, 0));
|
||||
@ -230,34 +231,34 @@ public class PathsTest {
|
||||
}
|
||||
|
||||
static Path2D readExpectedPath(final String resource) throws IOException {
|
||||
ObjectInputStream ois = new ObjectInputStream(PathsTest.class.getResourceAsStream(resource));
|
||||
|
||||
try {
|
||||
try (ObjectInputStream ois = new ObjectInputStream(PathsTest.class.getResourceAsStream(resource))) {
|
||||
return (Path2D) ois.readObject();
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
finally {
|
||||
ois.close();
|
||||
}
|
||||
}
|
||||
|
||||
static void assertPathEquals(final Path2D expectedPath, final Path2D actualPath) {
|
||||
PathIterator expectedIterator = expectedPath.getPathIterator(null);
|
||||
PathIterator actualIterator = actualPath.getPathIterator(null);
|
||||
|
||||
float[] expectedCoords = new float[6];
|
||||
float[] actualCoords = new float[6];
|
||||
|
||||
while(!actualIterator.isDone()) {
|
||||
while(!expectedIterator.isDone()) {
|
||||
assertFalse("Less points than expected", actualIterator.isDone());
|
||||
|
||||
int expectedType = expectedIterator.currentSegment(expectedCoords);
|
||||
int actualType = actualIterator.currentSegment(actualCoords);
|
||||
|
||||
assertEquals(expectedType, actualType);
|
||||
assertArrayEquals(expectedCoords, actualCoords, 0);
|
||||
assertEquals("Unexpected segment type", expectedType, actualType);
|
||||
assertArrayEquals("Unexpected coordinates", expectedCoords, actualCoords, 0);
|
||||
|
||||
actualIterator.next();
|
||||
expectedIterator.next();
|
||||
}
|
||||
|
||||
assertTrue("More points than expected", actualIterator.isDone());
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,6 @@ public final class PSDReader extends MetadataReader {
|
||||
|
||||
PSDResource resource = new PSDResource(id, input);
|
||||
entries.add(new PSDEntry(id, resource.name(), resource.data()));
|
||||
|
||||
}
|
||||
catch (EOFException e) {
|
||||
break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user