diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index 48d087e26..30b02145e 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -31,6 +31,7 @@ import java.lang.reflect.Method; import java.util.Iterator; import javax.imageio.*; +import javax.imageio.metadata.*; /** @@ -3089,8 +3090,6 @@ public class PImage implements PConstants, Cloneable { // JPEG and BMP images that have an alpha channel set get pretty unhappy. // BMP just doesn't write, and JPEG writes it as a CMYK image. // http://code.google.com/p/processing/issues/detail?id=415 -// String lower = path.toLowerCase(); -// if (lower.endsWith("bmp") || lower.endsWith("jpg") || lower.endsWith("jpeg")) { if (extension.equals("bmp") || extension.equals("jpg") || extension.equals("jpeg")) { outputFormat = BufferedImage.TYPE_INT_RGB; } @@ -3100,31 +3099,36 @@ public class PImage implements PConstants, Cloneable { File file = new File(path); + ImageWriter writer = null; + ImageWriteParam param = null; + IIOMetadata metadata = null; if (extension.equals("jpg") || extension.equals("jpeg")) { - ImageWriter writer = null; - Iterator iter = ImageIO.getImageWritersByFormatName("jpeg"); - if (iter.hasNext()) { - writer = iter.next(); - + if ((writer = imageioWriter("jpeg")) != null) { // Set JPEG quality to 90% with baseline optimization. Setting this // to 1 was a huge jump (about triple the size), so this seems good. - // Oddly, a smaller file size than Photoshop at 90%, but it's a - // completely different algorithm, I suppose. - ImageWriteParam param = writer.getDefaultWriteParam(); + // Oddly, a smaller file size than Photoshop at 90%, but I suppose + // it's a completely different algorithm. + param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(0.9f); - - BufferedOutputStream output = - new BufferedOutputStream(new FileOutputStream(file)); - writer.setOutput(ImageIO.createImageOutputStream(output)); - writer.write(null, new IIOImage(bimage, null, null), param); - writer.dispose(); - - output.flush(); - output.close(); - return true; } } + if ((writer = imageioWriter("png")) != null) { + param = writer.getDefaultWriteParam(); + metadata = imageioDPI(writer, param, 100); + } + if (writer != null) { + BufferedOutputStream output = + new BufferedOutputStream(PApplet.createOutput(file)); + writer.setOutput(ImageIO.createImageOutputStream(output)); +// writer.write(null, new IIOImage(bimage, null, null), param); + writer.write(metadata, new IIOImage(bimage, null, metadata), param); + writer.dispose(); + + output.flush(); + output.close(); + return true; + } // If iter.hasNext() somehow fails up top, it falls through to here return javax.imageio.ImageIO.write(bimage, extension, file); @@ -3135,6 +3139,52 @@ public class PImage implements PConstants, Cloneable { } + private ImageWriter imageioWriter(String extension) { + Iterator iter = ImageIO.getImageWritersByFormatName(extension); + if (iter.hasNext()) { + return iter.next(); + } + return null; + } + + + private IIOMetadata imageioDPI(ImageWriter writer, ImageWriteParam param, double dpi) { + // http://stackoverflow.com/questions/321736/how-to-set-dpi-information-in-an-image + ImageTypeSpecifier typeSpecifier = + ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB); + IIOMetadata metadata = + writer.getDefaultImageMetadata(typeSpecifier, param); + + if (!metadata.isReadOnly() && metadata.isStandardMetadataFormatSupported()) { + // for PNG, it's dots per millimeter + double dotsPerMilli = dpi / 25.4; + + IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize"); + horiz.setAttribute("value", Double.toString(dotsPerMilli)); + + IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize"); + vert.setAttribute("value", Double.toString(dotsPerMilli)); + + IIOMetadataNode dim = new IIOMetadataNode("Dimension"); + dim.appendChild(horiz); + dim.appendChild(vert); + + IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0"); + root.appendChild(dim); + + try { + metadata.mergeTree("javax_imageio_1.0", root); + return metadata; + + } catch (IIOInvalidTreeException e) { + System.err.println("Could not set the DPI of the output image"); + e.printStackTrace(); + } + } + return null; + } + + protected String[] saveImageFormats; /** diff --git a/core/todo.txt b/core/todo.txt index f75de5002..a17d18608 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -12,6 +12,12 @@ X getFloatContent() X getContent() or getStringContent()? X switch to CATEGORY instead of CATEGORICAL X removed createXML() and createTable()... just use 'new' for these +X implement means for setting dpi in PNG images +X need to add something for API yet +o JAI handles setting image size for png (check javax.imageio?) +o PNGEncodeParam png = PNGEncodeParam.getDefaultEncodeParam(bufImage); +o png.setPhysicalDimension(round(dpi*39.370079), round(dpi*39.370079), 1); +o JAI.create("filestore", bufImage, filename+".png", "PNG"); mouse wheel X add mouse wheel support to 2.0 event system @@ -44,6 +50,13 @@ X ref: "negative values if the mouse wheel was rotated up or away from the use X http://docs.oracle.com/javase/6/docs/api/java/awt/event/MouseWheelEvent.html#getWheelRotation() X http://docs.oracle.com/javase/7/docs/api/java/awt/event/MouseWheelEvent.html#getWheelRotation() +_ add options for image.save() (or sageImage?) +_ add quality=[0,1] for jpeg images +_ add dpi=[0,n] for for png images + +_ PImage.loadPixels() shouldn't be calling out to the renderer +_ setting image.parent = null for it makes it work again for get().save() case +_ PShape should be cached as well before release _ "deleted n framebuffer objects"