#490: Refactorings, added initial detection of linked/unlinked segments, more tests.

This commit is contained in:
Harald Kuhr
2020-01-09 19:17:35 +01:00
parent f15bcc7df9
commit 51ace4ca7f
11 changed files with 599 additions and 80 deletions
@@ -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());
}
}