#490: Fix for "incomplete" paths with implicit line back to start.

This commit is contained in:
Harald Kuhr 2020-01-22 20:06:30 +01:00
parent 420f78be88
commit 8b86b57e63
4 changed files with 228 additions and 77 deletions

View File

@ -67,11 +67,12 @@ public final class AdobePathWriter {
* regardless of image dimensions. * regardless of image dimensions.
* </p> * </p>
* *
* @param path A {@code Shape} instance that has {@link Path2D#WIND_EVEN_ODD WIND_EVEN_ODD} rule * @param path A {@code Shape} instance that has {@link Path2D#WIND_EVEN_ODD WIND_EVEN_ODD} rule,
* and is contained within the rectangle [x=0.0,y=0.0,w=1.0,h=1.0]. * is contained within the rectangle [x=0.0,y=0.0,w=1.0,h=1.0], and is closed.
* @throws IllegalArgumentException if {@code path} is {@code null}, * @throws IllegalArgumentException if {@code path} is {@code null},
* the paths winding rule is not @link Path2D#WIND_EVEN_ODD} or * the paths winding rule is not @link Path2D#WIND_EVEN_ODD} or
* the paths bounding box is outside [x=0.0,y=0.0,w=1.0,h=1.0]. * the paths bounding box is outside [x=0.0,y=0.0,w=1.0,h=1.0] or
* the path is not closed.
*/ */
public AdobePathWriter(final Shape path) { public AdobePathWriter(final Shape path) {
notNull(path, "path"); notNull(path, "path");
@ -128,8 +129,8 @@ public final class AdobePathWriter {
break; break;
case PathIterator.SEG_CLOSE: case PathIterator.SEG_CLOSE:
// Replace initial point.
AdobePathSegment initial = subpath.get(0); AdobePathSegment initial = subpath.get(0);
if (initial.apx != prev.apx || initial.apy != prev.apy) { if (initial.apx != prev.apx || initial.apy != prev.apy) {
// Line back to initial if last anchor point does not equal initial anchor // Line back to initial if last anchor point does not equal initial anchor
collinear = isCollinear(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.apx, initial.apy); collinear = isCollinear(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.apx, initial.apy);
@ -137,13 +138,7 @@ public final class AdobePathWriter {
prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, initial.apy, initial.apx, initial.apy, initial.apx, 0, 0); prev = new AdobePathSegment(CLOSED_SUBPATH_BEZIER_LINKED, initial.apy, initial.apx, initial.apy, initial.apx, 0, 0);
} }
collinear = isCollinear(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.cplx, initial.cply); close(initial, prev, subpath, segments);
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()));
segments.addAll(subpath);
subpath.clear(); subpath.clear();
break; break;
@ -152,12 +147,31 @@ public final class AdobePathWriter {
pathIterator.next(); pathIterator.next();
} }
// TODO: If subpath is not empty at this point, there was no close segment... // If subpath is not empty at this point, there was no close segment...
// Either wrap up (if coordinates match), or throw exception (otherwise) // Wrap up if coordinates match, otherwise throw exception
if (!subpath.isEmpty()) {
AdobePathSegment initial = subpath.get(0);
if (initial.apx != prev.apx || initial.apy != prev.apy) {
throw new IllegalArgumentException("Path must be closed");
}
close(initial, prev, subpath, segments);
}
return segments; return segments;
} }
private static void close(AdobePathSegment initial, AdobePathSegment prev, List<AdobePathSegment> subpath, List<AdobePathSegment> segments) {
// Replace initial point.
boolean collinear = isCollinear(prev.cppx, prev.cppy, initial.apx, initial.apy, initial.cplx, initial.cply);
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()));
segments.addAll(subpath);
}
private static boolean isCollinear(double x1, double y1, double x2, double y2, double x3, double y3) { private static boolean isCollinear(double x1, double y1, double x2, double y2, double x3, double y3) {
// Photoshop seems to write as linked if all points are the same.... // Photoshop seems to write as linked if all points are the same....
return (x1 == x2 && x2 == x3 && y1 == y2 && y2 == y3) || return (x1 == x2 && x2 == x3 && y1 == y2 && y2 == y3) ||
@ -248,5 +262,4 @@ public final class AdobePathWriter {
return bytes.toByteArray(); return bytes.toByteArray();
} }
} }

View File

@ -53,6 +53,7 @@ import java.awt.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -258,7 +259,30 @@ public final class Paths {
return applyClippingPath(clip, image); return applyClippingPath(clip, image);
} }
public static boolean writeClipped(final BufferedImage image, Shape clipPath, final String formatName, final ImageOutputStream output) throws IOException { /**
* Writes the image along with a clipping path resource, in the given format to the supplied output.
* The image is written to the
* {@code ImageOutputStream} starting at the current stream
* pointer, overwriting existing stream data from that point
* forward, if present.
* <p>
* Note: As {@link ImageIO#write(RenderedImage, String, ImageOutputStream)}, this method does
* <em>not</em> close the output stream.
* It is the responsibility of the caller to close the stream, if desired.
* </p>
*
* @param image the image to be written, may not be {@code null}.
* @param clipPath the clip path, may not be {@code null}.
* @param formatName the informal format name, may not be {@code null}.
* @param output the stream to write to, may not be {@code null}.
*
* @return {@code true} if the image was written,
* otherwise {@code false} (ie. no writer was found for the specified format).
*
* @exception IllegalArgumentException if any parameter is {@code null}.
* @exception IOException if an error occurs during writing.
*/
public static boolean writeClipped(final RenderedImage image, Shape clipPath, final String formatName, final ImageOutputStream output) throws IOException {
if (image == null) { if (image == null) {
throw new IllegalArgumentException("image == null!"); throw new IllegalArgumentException("image == null!");
} }
@ -269,74 +293,75 @@ public final class Paths {
throw new IllegalArgumentException("output == null!"); throw new IllegalArgumentException("output == null!");
} }
String format = "JPG".equalsIgnoreCase(formatName) ? "JPEG" : formatName.toUpperCase(); ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(image);
Iterator<ImageWriter> writers = ImageIO.getImageWriters(type, formatName);
if ("TIFF".equals(format) || "JPEG".equals(format)) { if (writers.hasNext()) {
ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(image); ImageWriter writer = writers.next();
Iterator<ImageWriter> writers = ImageIO.getImageWriters(type, formatName);
if (writers.hasNext()) { ImageWriteParam param = writer.getDefaultWriteParam();
ImageWriter writer = writers.next(); IIOMetadata metadata = writer.getDefaultImageMetadata(type, param);
List<String> metadataFormats = asList(metadata.getMetadataFormatNames());
ImageWriteParam param = writer.getDefaultWriteParam(); byte[] pathResource = new AdobePathWriter(clipPath).writePathResource();
IIOMetadata metadata = writer.getDefaultImageMetadata(type, param);
List<String> metadataFormats = asList(metadata.getMetadataFormatNames());
byte[] pathResource = new AdobePathWriter(clipPath).writePathResource(); if (metadataFormats.contains("javax_imageio_tiff_image_1.0") || metadataFormats.contains("com_sun_media_imageio_plugins_tiff_image_1.0")) {
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionType("Deflate");
if ("TIFF".equals(format)) { // Check if the format is that of the bundled TIFF writer, otherwise use JAI format
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); String metadataFormat = metadataFormats.contains("javax_imageio_tiff_image_1.0")
param.setCompressionType("Deflate"); ? "javax_imageio_tiff_image_1.0"
: "com_sun_media_imageio_plugins_tiff_image_1.0"; // Fails in mergeTree, if not supported
IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
IIOMetadataNode ifd = new IIOMetadataNode("TIFFIFD");
// Check if the format is that of the bundled TIFF writer, otherwise use JAI format IIOMetadataNode pathField = new IIOMetadataNode("TIFFField");
String metadataFormat = metadataFormats.contains("javax_imageio_tiff_image_1.0") pathField.setAttribute("number", String.valueOf(TIFF.TAG_PHOTOSHOP));
? "javax_imageio_tiff_image_1.0" IIOMetadataNode pathValue = new IIOMetadataNode("TIFFUndefined"); // Use undefined for simplicity, could also use bytes
: "com_sun_media_imageio_plugins_tiff_image_1.0"; // Fails in mergeTree, if not supported pathValue.setAttribute("value", arrayAsString(pathResource));
IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
IIOMetadataNode ifd = new IIOMetadataNode("TIFFIFD");
IIOMetadataNode pathField = new IIOMetadataNode("TIFFField"); pathField.appendChild(pathValue);
pathField.setAttribute("number", String.valueOf(TIFF.TAG_PHOTOSHOP)); ifd.appendChild(pathField);
IIOMetadataNode pathValue = new IIOMetadataNode("TIFFUndefined"); // Use undefined for simplicity, could also use bytes root.appendChild(ifd);
pathValue.setAttribute("value", arrayAsString(pathResource));
pathField.appendChild(pathValue); metadata.mergeTree(metadataFormat, root);
ifd.appendChild(pathField);
root.appendChild(ifd);
metadata.mergeTree(metadataFormat, root);
}
else if ("JPEG".equals(format)) {
String metadataFormat = "javax_imageio_jpeg_image_1.0";
IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
root.appendChild(new IIOMetadataNode("JPEGvariety"));
IIOMetadataNode sequence = new IIOMetadataNode("markerSequence");
// App13/Photshop 3.0
IIOMetadataNode unknown = new IIOMetadataNode("unknown");
unknown.setAttribute("MarkerTag", Integer.toString(JPEG.APP13 & 0xFF));
byte[] identfier = "Photoshop 3.0".getBytes(StandardCharsets.US_ASCII);
byte[] data = new byte[identfier.length + 1 + pathResource.length];
System.arraycopy(identfier, 0, data, 0, identfier.length);
System.arraycopy(pathResource, 0, data, identfier.length + 1, pathResource.length);
unknown.setUserObject(data);
sequence.appendChild(unknown);
root.appendChild(sequence);
metadata.mergeTree(metadataFormat, root);
}
// TODO: Else if PSD... Requires PSD write + new metadata format...
writer.setOutput(output); writer.setOutput(output);
writer.write(null, new IIOImage(image, null, metadata), param); writer.write(null, new IIOImage(image, null, metadata), param);
return true; return true;
} }
else if (metadataFormats.contains("javax_imageio_jpeg_image_1.0")) {
String metadataFormat = "javax_imageio_jpeg_image_1.0";
IIOMetadataNode root = new IIOMetadataNode(metadataFormat);
root.appendChild(new IIOMetadataNode("JPEGvariety"));
IIOMetadataNode sequence = new IIOMetadataNode("markerSequence");
// App13/Photshop 3.0
IIOMetadataNode unknown = new IIOMetadataNode("unknown");
unknown.setAttribute("MarkerTag", Integer.toString(JPEG.APP13 & 0xFF));
byte[] identfier = "Photoshop 3.0".getBytes(StandardCharsets.US_ASCII);
byte[] data = new byte[identfier.length + 1 + pathResource.length];
System.arraycopy(identfier, 0, data, 0, identfier.length);
System.arraycopy(pathResource, 0, data, identfier.length + 1, pathResource.length);
unknown.setUserObject(data);
sequence.appendChild(unknown);
root.appendChild(sequence);
metadata.mergeTree(metadataFormat, root);
writer.setOutput(output);
writer.write(null, new IIOImage(image, null, metadata), param);
return true;
}
// TODO: Else if PSD... Requires PSD write + new metadata format...
} }
return false; return false;

View File

@ -44,7 +44,8 @@ import java.util.Arrays;
import static com.twelvemonkeys.imageio.path.AdobePathSegment.*; import static com.twelvemonkeys.imageio.path.AdobePathSegment.*;
import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals; import static com.twelvemonkeys.imageio.path.PathsTest.assertPathEquals;
import static org.junit.Assert.*; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
/** /**
* AdobePathWriterTest. * AdobePathWriterTest.
@ -92,6 +93,16 @@ public class AdobePathWriterTest {
new AdobePathWriter(path); new AdobePathWriter(path);
} }
@Test(expected = IllegalArgumentException.class)
public void testCreateNotClosed() {
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD);
path.moveTo(.5, .5);
path.lineTo(1, .5);
path.curveTo(1, 1, 1, 1, .5, 1);
new AdobePathWriter(path).writePath();
}
@Test @Test
public void testCreateClosed() { public void testCreateClosed() {
GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD); GeneralPath path = new GeneralPath(Path2D.WIND_EVEN_ODD);
@ -100,9 +111,30 @@ public class AdobePathWriterTest {
path.curveTo(1, 1, 1, 1, .5, 1); path.curveTo(1, 1, 1, 1, .5, 1);
path.closePath(); path.closePath();
new AdobePathWriter(path).writePath(); byte[] bytes = new AdobePathWriter(path).writePath();
fail("Test that we have 4 segments"); assertEquals(6 * 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, 3, 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, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 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, 1, 0, 0, 0, 1, 0, 0, 0},
Arrays.copyOfRange(bytes, off, off += 26));
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0},
Arrays.copyOfRange(bytes, off, off += 26));
// Sanity
assertEquals(bytes.length, off);
} }
@Test @Test
@ -113,9 +145,31 @@ public class AdobePathWriterTest {
path.curveTo(1, 1, 1, 1, .5, 1); path.curveTo(1, 1, 1, 1, .5, 1);
path.lineTo(.5, .5); path.lineTo(.5, .5);
new AdobePathWriter(path).writePath(); // TODO: Should we allow this? byte[] bytes = new AdobePathWriter(path).writePath();
assertEquals(6 * 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, 3, 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, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 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, 1, 0, 0, 0, 1, 0, 0, 0},
Arrays.copyOfRange(bytes, off, off += 26));
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0},
Arrays.copyOfRange(bytes, off, off += 26));
// Sanity
assertEquals(bytes.length, off);
fail("Test that we have 4 segments, and that it is equal to the one above");
} }
@Test @Test
@ -127,9 +181,30 @@ public class AdobePathWriterTest {
path.lineTo(.5, .5); path.lineTo(.5, .5);
path.closePath(); path.closePath();
new AdobePathWriter(path).writePath(); byte[] bytes = new AdobePathWriter(path).writePath();
fail("Test that we have 4 segments, and that it is equal to the one above"); assertEquals(6 * 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, 3, 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, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 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, 1, 0, 0, 0, 1, 0, 0, 0},
Arrays.copyOfRange(bytes, off, off += 26));
assertArrayEquals(new byte[] {0, CLOSED_SUBPATH_BEZIER_UNLINKED, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0, 0, -128, 0, 0},
Arrays.copyOfRange(bytes, off, off += 26));
// Sanity
assertEquals(bytes.length, off);
} }
@Test @Test
@ -207,7 +282,6 @@ public class AdobePathWriterTest {
AdobePathWriter pathCreator = new AdobePathWriter(path); AdobePathWriter pathCreator = new AdobePathWriter(path);
byte[] bytes = pathCreator.writePath(); byte[] bytes = pathCreator.writePath();
// System.err.println(Arrays.toString(bytes));
assertEquals(12 * 26, bytes.length); assertEquals(12 * 26, bytes.length);

View File

@ -38,15 +38,18 @@ import org.junit.Test;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.spi.IIORegistry; import javax.imageio.spi.IIORegistry;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*; import java.awt.*;
import java.awt.geom.GeneralPath; import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.awt.geom.PathIterator; import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
/** /**
* PathsTest. * PathsTest.
@ -240,6 +243,9 @@ public class PathsTest {
} }
static void assertPathEquals(final Path2D expectedPath, final Path2D actualPath) { static void assertPathEquals(final Path2D expectedPath, final Path2D actualPath) {
assertNotNull("Expected path is null, check your tests...", expectedPath);
assertNotNull(actualPath);
PathIterator expectedIterator = expectedPath.getPathIterator(null); PathIterator expectedIterator = expectedPath.getPathIterator(null);
PathIterator actualIterator = actualPath.getPathIterator(null); PathIterator actualIterator = actualPath.getPathIterator(null);
@ -261,4 +267,37 @@ public class PathsTest {
assertTrue("More points than expected", actualIterator.isDone()); assertTrue("More points than expected", actualIterator.isDone());
} }
@Test
public void testWriteJPEG() throws IOException {
Path2D originalPath = readExpectedPath("/ser/multiple-clips.ser");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_3BYTE_BGR);
try (ImageOutputStream stream = ImageIO.createImageOutputStream(bytes)) {
boolean written = Paths.writeClipped(image, originalPath, "JPEG", stream);
assertTrue(written);
}
assertTrue(bytes.size() > 1024); // Actual size may be plugin specific...
Path2D actualPath = Paths.readPath(new ByteArrayImageInputStream(bytes.toByteArray()));
assertPathEquals(originalPath, actualPath);
}
@Test
public void testWriteTIFF() throws IOException {
Path2D originalPath = readExpectedPath("/ser/grape-path.ser");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
try (ImageOutputStream stream = ImageIO.createImageOutputStream(bytes)) {
boolean written = Paths.writeClipped(image, originalPath, "TIFF", stream);
assumeTrue(written); // TIFF support is optional
}
assertTrue(bytes.size() > 1024); // Actual size may be plugin specific...
Path2D actualPath = Paths.readPath(new ByteArrayImageInputStream(bytes.toByteArray()));
assertPathEquals(originalPath, actualPath);
}
} }