From e46eceab968ede535bf2ff262ee08d7616a76a99 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sun, 19 Jan 2020 21:24:35 -0500 Subject: [PATCH] a little scarier: moving java.awt and javax.imageio out of PImage --- core/src/processing/awt/PGraphicsJava2D.java | 16 +- core/src/processing/awt/PImageAWT.java | 381 +++++++++++++++++++ core/src/processing/awt/ShimAWT.java | 2 +- core/src/processing/core/PImage.java | 335 +--------------- core/src/processing/core/PShape.java | 5 +- core/src/processing/opengl/PSurfaceJOGL.java | 4 +- core/todo.txt | 10 +- 7 files changed, 413 insertions(+), 340 deletions(-) create mode 100644 core/src/processing/awt/PImageAWT.java diff --git a/core/src/processing/awt/PGraphicsJava2D.java b/core/src/processing/awt/PGraphicsJava2D.java index 7d6e1c597..264c12c72 100644 --- a/core/src/processing/awt/PGraphicsJava2D.java +++ b/core/src/processing/awt/PGraphicsJava2D.java @@ -253,14 +253,14 @@ public class PGraphicsJava2D extends PGraphics { } - /** - * Still need a means to get the java.awt.Image object, since getNative() - * is going to return the {@link Graphics2D} object. - */ - @Override - public Image getImage() { - return image; - } +// /** +// * Still need a means to get the java.awt.Image object, since getNative() +// * is going to return the {@link Graphics2D} object. +// */ +// @Override +// public Image getImage() { +// return image; +// } /** Returns the java.awt.Graphics2D object used by this renderer. */ diff --git a/core/src/processing/awt/PImageAWT.java b/core/src/processing/awt/PImageAWT.java new file mode 100644 index 000000000..7489ebe8c --- /dev/null +++ b/core/src/processing/awt/PImageAWT.java @@ -0,0 +1,381 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2015 The Processing Foundation + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.awt; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.awt.image.PixelGrabber; +import java.awt.image.WritableRaster; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOInvalidTreeException; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; + +import processing.core.PApplet; +import processing.core.PImage; + + +public class PImageAWT extends PImage { + + /** + * Construct a new PImage from a java.awt.Image. This constructor assumes + * that you've done the work of making sure a MediaTracker has been used + * to fully download the data and that the img is valid. + * + * @nowebref + * @param img assumes a MediaTracker has been used to fully download + * the data and the img is valid + */ + public PImageAWT(Image img) { + format = RGB; + if (img instanceof BufferedImage) { + BufferedImage bi = (BufferedImage) img; + width = bi.getWidth(); + height = bi.getHeight(); + int type = bi.getType(); + if (type == BufferedImage.TYPE_3BYTE_BGR || + type == BufferedImage.TYPE_4BYTE_ABGR) { + pixels = new int[width * height]; + bi.getRGB(0, 0, width, height, pixels, 0, width); + if (type == BufferedImage.TYPE_4BYTE_ABGR) { + format = ARGB; + } else { + opaque(); + } + } else { + DataBuffer db = bi.getRaster().getDataBuffer(); + if (db instanceof DataBufferInt) { + pixels = ((DataBufferInt) db).getData(); + if (type == BufferedImage.TYPE_INT_ARGB) { + format = ARGB; + } else if (type == BufferedImage.TYPE_INT_RGB) { + opaque(); + } + } + } + } + // Implements fall-through if not DataBufferInt above, or not a + // known type, or not DataBufferInt for the data itself. + if (pixels == null) { // go the old school Java 1.0 route + width = img.getWidth(null); + height = img.getHeight(null); + pixels = new int[width * height]; + PixelGrabber pg = + new PixelGrabber(img, 0, 0, width, height, pixels, 0, width); + try { + pg.grabPixels(); + } catch (InterruptedException e) { } + } + pixelDensity = 1; + pixelWidth = width; + pixelHeight = height; + } + + + /** + * Use the getNative() method instead, which allows library interfaces to be + * written in a cross-platform fashion for desktop, Android, and others. + * This is still included for PGraphics objects, which may need the image. + */ + public Image getImage() { // ignore + return (Image) getNative(); + } + + + /** + * Returns a native BufferedImage from this PImage. + */ + @Override + public Object getNative() { // ignore + loadPixels(); + int type = (format == RGB) ? + BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; + BufferedImage image = new BufferedImage(pixelWidth, pixelHeight, type); + WritableRaster wr = image.getRaster(); + wr.setDataElements(0, 0, pixelWidth, pixelHeight, pixels); + return image; + } + + + @Override + public void resize(int w, int h) { // ignore + if (w <= 0 && h <= 0) { + throw new IllegalArgumentException("width or height must be > 0 for resize"); + } + + if (w == 0) { // Use height to determine relative size + float diff = (float) h / (float) height; + w = (int) (width * diff); + } else if (h == 0) { // Use the width to determine relative size + float diff = (float) w / (float) width; + h = (int) (height * diff); + } + + BufferedImage img = + shrinkImage((BufferedImage) getNative(), w*pixelDensity, h*pixelDensity); + + PImage temp = new PImageAWT(img); + this.pixelWidth = temp.width; + this.pixelHeight = temp.height; + + // Get the resized pixel array + this.pixels = temp.pixels; + + this.width = pixelWidth / pixelDensity; + this.height = pixelHeight / pixelDensity; + + // Mark the pixels array as altered + updatePixels(); + } + + + // Adapted from getFasterScaledInstance() method from page 111 of + // "Filthy Rich Clients" by Chet Haase and Romain Guy + // Additional modifications and simplifications have been added, + // plus a fix to deal with an infinite loop if images are expanded. + // http://code.google.com/p/processing/issues/detail?id=1463 + static private BufferedImage shrinkImage(BufferedImage img, + int targetWidth, int targetHeight) { + int type = (img.getTransparency() == Transparency.OPAQUE) ? + BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; + BufferedImage outgoing = img; + BufferedImage scratchImage = null; + Graphics2D g2 = null; + int prevW = outgoing.getWidth(); + int prevH = outgoing.getHeight(); + boolean isTranslucent = img.getTransparency() != Transparency.OPAQUE; + + // Use multi-step technique: start with original size, then scale down in + // multiple passes with drawImage() until the target size is reached + int w = img.getWidth(); + int h = img.getHeight(); + + do { + if (w > targetWidth) { + w /= 2; + // if this is the last step, do the exact size + if (w < targetWidth) { + w = targetWidth; + } + } else if (targetWidth >= w) { + w = targetWidth; + } + if (h > targetHeight) { + h /= 2; + if (h < targetHeight) { + h = targetHeight; + } + } else if (targetHeight >= h) { + h = targetHeight; + } + if (scratchImage == null || isTranslucent) { + // Use a single scratch buffer for all iterations and then copy + // to the final, correctly-sized image before returning + scratchImage = new BufferedImage(w, h, type); + g2 = scratchImage.createGraphics(); + } + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(outgoing, 0, 0, w, h, 0, 0, prevW, prevH, null); + prevW = w; + prevH = h; + outgoing = scratchImage; + } while (w != targetWidth || h != targetHeight); + + if (g2 != null) { + g2.dispose(); + } + + // If we used a scratch buffer that is larger than our target size, + // create an image of the right size and copy the results into it + if (targetWidth != outgoing.getWidth() || + targetHeight != outgoing.getHeight()) { + scratchImage = new BufferedImage(targetWidth, targetHeight, type); + g2 = scratchImage.createGraphics(); + g2.drawImage(outgoing, 0, 0, null); + g2.dispose(); + outgoing = scratchImage; + } + return outgoing; + } + + + @Override + protected boolean saveImpl(String filename) { + if (saveImageFormats == null) { + saveImageFormats = javax.imageio.ImageIO.getWriterFormatNames(); + } + try { + if (saveImageFormats != null) { + for (int i = 0; i < saveImageFormats.length; i++) { + if (filename.endsWith("." + saveImageFormats[i])) { + if (!saveImageIO(filename)) { + System.err.println("Error while saving image."); + return false; + } + return true; + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + + protected String[] saveImageFormats; + + + /** + * Use ImageIO functions from Java 1.4 and later to handle image save. + * Various formats are supported, typically jpeg, png, bmp, and wbmp. + * To get a list of the supported formats for writing, use:
+ * println(javax.imageio.ImageIO.getReaderFormatNames()) + */ + protected boolean saveImageIO(String path) throws IOException { + try { + int outputFormat = (format == ARGB) ? + BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB; + + String extension = + path.substring(path.lastIndexOf('.') + 1).toLowerCase(); + + // 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 + if (extension.equals("bmp") || extension.equals("jpg") || extension.equals("jpeg")) { + outputFormat = BufferedImage.TYPE_INT_RGB; + } + + BufferedImage bimage = new BufferedImage(pixelWidth, pixelHeight, outputFormat); + bimage.setRGB(0, 0, pixelWidth, pixelHeight, pixels, 0, pixelWidth); + + File file = new File(path); + + ImageWriter writer = null; + ImageWriteParam param = null; + IIOMetadata metadata = null; + + if (extension.equals("jpg") || extension.equals("jpeg")) { + 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 I suppose + // it's a completely different algorithm. + param = writer.getDefaultWriteParam(); + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + param.setCompressionQuality(0.9f); + } + } + + if (extension.equals("png")) { + if ((writer = imageioWriter("png")) != null) { + param = writer.getDefaultWriteParam(); + if (false) { + 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); + + } catch (Exception e) { + e.printStackTrace(); + throw new IOException("image save failed."); + } + } + + + 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; + } +} \ No newline at end of file diff --git a/core/src/processing/awt/ShimAWT.java b/core/src/processing/awt/ShimAWT.java index e3392f1d4..c9a179ae5 100644 --- a/core/src/processing/awt/ShimAWT.java +++ b/core/src/processing/awt/ShimAWT.java @@ -131,7 +131,7 @@ public class ShimAWT implements PConstants { } } - PImage image = new PImage(awtImage); + PImage image = new PImageAWT(awtImage); if (image.width == -1) { System.err.println("The file " + filename + " contains bad image data, or may not be an image."); diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index 359cccca8..01fc83a66 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -24,31 +24,12 @@ package processing.core; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.RenderingHints; -import java.awt.Transparency; -import java.awt.image.BufferedImage; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferInt; -import java.awt.image.PixelGrabber; -import java.awt.image.WritableRaster; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Iterator; - -import javax.imageio.IIOImage; -import javax.imageio.ImageIO; -import javax.imageio.ImageTypeSpecifier; -import javax.imageio.ImageWriteParam; -import javax.imageio.ImageWriter; -import javax.imageio.metadata.IIOInvalidTreeException; -import javax.imageio.metadata.IIOMetadata; -import javax.imageio.metadata.IIOMetadataNode; /** @@ -350,82 +331,9 @@ public class PImage implements PConstants, Cloneable { this.pixels = pixels; } - /** - * Construct a new PImage from a java.awt.Image. This constructor assumes - * that you've done the work of making sure a MediaTracker has been used - * to fully download the data and that the img is valid. - * - * @nowebref - * @param img assumes a MediaTracker has been used to fully download - * the data and the img is valid - */ - public PImage(Image img) { - format = RGB; - if (img instanceof BufferedImage) { - BufferedImage bi = (BufferedImage) img; - width = bi.getWidth(); - height = bi.getHeight(); - int type = bi.getType(); - if (type == BufferedImage.TYPE_3BYTE_BGR || - type == BufferedImage.TYPE_4BYTE_ABGR) { - pixels = new int[width * height]; - bi.getRGB(0, 0, width, height, pixels, 0, width); - if (type == BufferedImage.TYPE_4BYTE_ABGR) { - format = ARGB; - } else { - opaque(); - } - } else { - DataBuffer db = bi.getRaster().getDataBuffer(); - if (db instanceof DataBufferInt) { - pixels = ((DataBufferInt) db).getData(); - if (type == BufferedImage.TYPE_INT_ARGB) { - format = ARGB; - } else if (type == BufferedImage.TYPE_INT_RGB) { - opaque(); - } - } - } - } - // Implements fall-through if not DataBufferInt above, or not a - // known type, or not DataBufferInt for the data itself. - if (pixels == null) { // go the old school Java 1.0 route - width = img.getWidth(null); - height = img.getHeight(null); - pixels = new int[width * height]; - PixelGrabber pg = - new PixelGrabber(img, 0, 0, width, height, pixels, 0, width); - try { - pg.grabPixels(); - } catch (InterruptedException e) { } - } - pixelDensity = 1; - pixelWidth = width; - pixelHeight = height; - } - - /** - * Use the getNative() method instead, which allows library interfaces to be - * written in a cross-platform fashion for desktop, Android, and others. - * This is still included for PGraphics objects, which may need the image. - */ - public Image getImage() { // ignore - return (Image) getNative(); - } - - - /** - * Returns a native BufferedImage from this PImage. - */ public Object getNative() { // ignore - loadPixels(); - int type = (format == RGB) ? - BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; - BufferedImage image = new BufferedImage(pixelWidth, pixelHeight, type); - WritableRaster wr = image.getRaster(); - wr.setDataElements(0, 0, pixelWidth, pixelHeight, pixels); - return image; + return null; } @@ -610,104 +518,7 @@ public class PImage implements PConstants, Cloneable { * @see PImage#get(int, int, int, int) */ public void resize(int w, int h) { // ignore - if (w <= 0 && h <= 0) { - throw new IllegalArgumentException("width or height must be > 0 for resize"); - } - - if (w == 0) { // Use height to determine relative size - float diff = (float) h / (float) height; - w = (int) (width * diff); - } else if (h == 0) { // Use the width to determine relative size - float diff = (float) w / (float) width; - h = (int) (height * diff); - } - - BufferedImage img = - shrinkImage((BufferedImage) getNative(), w*pixelDensity, h*pixelDensity); - - PImage temp = new PImage(img); - this.pixelWidth = temp.width; - this.pixelHeight = temp.height; - - // Get the resized pixel array - this.pixels = temp.pixels; - - this.width = pixelWidth / pixelDensity; - this.height = pixelHeight / pixelDensity; - - // Mark the pixels array as altered - updatePixels(); - } - - - // Adapted from getFasterScaledInstance() method from page 111 of - // "Filthy Rich Clients" by Chet Haase and Romain Guy - // Additional modifications and simplifications have been added, - // plus a fix to deal with an infinite loop if images are expanded. - // http://code.google.com/p/processing/issues/detail?id=1463 - static private BufferedImage shrinkImage(BufferedImage img, - int targetWidth, int targetHeight) { - int type = (img.getTransparency() == Transparency.OPAQUE) ? - BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; - BufferedImage outgoing = img; - BufferedImage scratchImage = null; - Graphics2D g2 = null; - int prevW = outgoing.getWidth(); - int prevH = outgoing.getHeight(); - boolean isTranslucent = img.getTransparency() != Transparency.OPAQUE; - - // Use multi-step technique: start with original size, then scale down in - // multiple passes with drawImage() until the target size is reached - int w = img.getWidth(); - int h = img.getHeight(); - - do { - if (w > targetWidth) { - w /= 2; - // if this is the last step, do the exact size - if (w < targetWidth) { - w = targetWidth; - } - } else if (targetWidth >= w) { - w = targetWidth; - } - if (h > targetHeight) { - h /= 2; - if (h < targetHeight) { - h = targetHeight; - } - } else if (targetHeight >= h) { - h = targetHeight; - } - if (scratchImage == null || isTranslucent) { - // Use a single scratch buffer for all iterations and then copy - // to the final, correctly-sized image before returning - scratchImage = new BufferedImage(w, h, type); - g2 = scratchImage.createGraphics(); - } - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(outgoing, 0, 0, w, h, 0, 0, prevW, prevH, null); - prevW = w; - prevH = h; - outgoing = scratchImage; - } while (w != targetWidth || h != targetHeight); - - if (g2 != null) { - g2.dispose(); - } - - // If we used a scratch buffer that is larger than our target size, - // create an image of the right size and copy the results into it - if (targetWidth != outgoing.getWidth() || - targetHeight != outgoing.getHeight()) { - scratchImage = new BufferedImage(targetWidth, targetHeight, type); - g2 = scratchImage.createGraphics(); - g2.drawImage(outgoing, 0, 0, null); - g2.dispose(); - outgoing = scratchImage; - } - return outgoing; + throw new RuntimeException("resize() not implemented for this PImage type"); } @@ -3010,6 +2821,12 @@ int testFunction(int dst, int src) { // FILE I/O + + protected boolean saveImpl(String filename) { + return false; + } + + static public PImage loadTIFF(InputStream input) { byte tiff[] = PApplet.loadBytes(input); @@ -3453,127 +3270,6 @@ int testFunction(int dst, int src) { } - /** - * Use ImageIO functions from Java 1.4 and later to handle image save. - * Various formats are supported, typically jpeg, png, bmp, and wbmp. - * To get a list of the supported formats for writing, use:
- * println(javax.imageio.ImageIO.getReaderFormatNames()) - */ - protected boolean saveImageIO(String path) throws IOException { - try { - int outputFormat = (format == ARGB) ? - BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB; - - String extension = - path.substring(path.lastIndexOf('.') + 1).toLowerCase(); - - // 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 - if (extension.equals("bmp") || extension.equals("jpg") || extension.equals("jpeg")) { - outputFormat = BufferedImage.TYPE_INT_RGB; - } - - BufferedImage bimage = new BufferedImage(pixelWidth, pixelHeight, outputFormat); - bimage.setRGB(0, 0, pixelWidth, pixelHeight, pixels, 0, pixelWidth); - - File file = new File(path); - - ImageWriter writer = null; - ImageWriteParam param = null; - IIOMetadata metadata = null; - - if (extension.equals("jpg") || extension.equals("jpeg")) { - 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 I suppose - // it's a completely different algorithm. - param = writer.getDefaultWriteParam(); - param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - param.setCompressionQuality(0.9f); - } - } - - if (extension.equals("png")) { - if ((writer = imageioWriter("png")) != null) { - param = writer.getDefaultWriteParam(); - if (false) { - 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); - - } catch (Exception e) { - e.printStackTrace(); - throw new IOException("image save failed."); - } - } - - - 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; - /** * ( begin auto-generated from PImage_save.xml ) * @@ -3646,19 +3342,8 @@ int testFunction(int dst, int src) { try { OutputStream os = null; - if (saveImageFormats == null) { - saveImageFormats = javax.imageio.ImageIO.getWriterFormatNames(); - } - if (saveImageFormats != null) { - for (int i = 0; i < saveImageFormats.length; i++) { - if (filename.endsWith("." + saveImageFormats[i])) { - if (!saveImageIO(filename)) { - System.err.println("Error while saving image."); - return false; - } - return true; - } - } + if (saveImpl(filename)) { + return true; } if (filename.toLowerCase().endsWith(".tga")) { diff --git a/core/src/processing/core/PShape.java b/core/src/processing/core/PShape.java index babbe516d..ed2507bac 100644 --- a/core/src/processing/core/PShape.java +++ b/core/src/processing/core/PShape.java @@ -29,6 +29,9 @@ import java.util.HashMap; import java.util.Map; import javax.swing.ImageIcon; + +import processing.awt.PImageAWT; + import java.util.Base64; @@ -1950,7 +1953,7 @@ public class PShape implements PConstants { boolean requiresCheckAlpha = extension.equals("gif") || extension.equals("png") || extension.equals("unknown"); - PImage loadedImage = new PImage(awtImage); + PImage loadedImage = new PImageAWT(awtImage); if (requiresCheckAlpha) { loadedImage.checkAlpha(); diff --git a/core/src/processing/opengl/PSurfaceJOGL.java b/core/src/processing/opengl/PSurfaceJOGL.java index 71bbcd168..ae632d9a0 100644 --- a/core/src/processing/opengl/PSurfaceJOGL.java +++ b/core/src/processing/opengl/PSurfaceJOGL.java @@ -76,7 +76,7 @@ import processing.core.PImage; import processing.core.PSurface; import processing.event.KeyEvent; import processing.event.MouseEvent; - +import processing.awt.PImageAWT; // have this removed by 4.0 final import processing.awt.ShimAWT; @@ -1338,7 +1338,7 @@ public class PSurfaceJOGL implements PSurface { if (name != null) { ImageIcon icon = new ImageIcon(getClass().getResource("cursors/" + name + ".png")); - PImage img = new PImage(icon.getImage()); + PImage img = new PImageAWT(icon.getImage()); // Most cursors just use the center as the hotspot... int x = img.width / 2; int y = img.height / 2; diff --git a/core/todo.txt b/core/todo.txt index 6bef9b255..08ab4ed05 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -14,24 +14,28 @@ X remove the java.awt.Frame object from PApplet X move loadImage() into ShimAWT X desktopFile() and desktopPath() methods are supported, unless we find they're trouble X move ShimAWT.loadImage() to the PSurface subclasses +X move all java.awt and javax.imageio out of PImage api changes _ static versions of selectInput/selectOutput/selectFolder in PApplet have been removed _ java.awt.Frame object "frame" removed from PApplet (been warning since 2015) _ protected PImage.checkAlpha() now public +_ all AWT calls have been moved out of PImage +_ this may be a problem for anything that was relying on those internals +api todo _ Surface not available inside handleSettings() _ therefore impossible to get displays, displayWidth/Height, and pixelDensity _ should surface be set on command line? _ or can we just require that size() goes first, and inits surface *type* w/o sizes _ or even that it inits a surface-specific class for getting that info - - -before final release _ implement selectInput/Output/Folder methods in PSurfaceJOGL _ implement selectInput/Output/Folder methods in PSurfaceFX _ implement openLink() in PSurfaceFX _ implement openLink() in PSurfaceJOGL + + +before final release _ Intel HD Graphics 3000 workaround is causing a big fat warning _ https://github.com/processing/processing4/issues/50 _ ThinkDifferent unavailable with --disable-awt, needs workaround