From bec29c4cd33c9b95cc7cb9d5aa62f0904bb60414 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 10 May 2017 15:20:59 +0200 Subject: [PATCH] Added image utilities --- src/dorkbox/util/ImageUtil.java | 219 ++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 src/dorkbox/util/ImageUtil.java diff --git a/src/dorkbox/util/ImageUtil.java b/src/dorkbox/util/ImageUtil.java new file mode 100644 index 0000000..8b7c355 --- /dev/null +++ b/src/dorkbox/util/ImageUtil.java @@ -0,0 +1,219 @@ +/* + * Copyright 2016 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.util; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Iterator; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import javax.swing.ImageIcon; + +@SuppressWarnings("WeakerAccess") +public +class ImageUtil { + + /** + * Resizes the image, as either a a FILE on disk, or as a RESOURCE name, and saves the new size as a file on disk. This new file will + * replace any other file with the same name. + * + * @return the file string on disk that is the resized icon + */ + public static + String resizeFileOrResource(final int sizeWidth, final String fileName) throws IOException { + FileInputStream fileInputStream = new FileInputStream(fileName); + + Dimension imageSize = getImageSize(fileInputStream); + //noinspection NumericCastThatLosesPrecision + if (sizeWidth == ((int) imageSize.getWidth())) { + // we can reuse this file. + return fileName; + } + + // have to resize the file (and return the new path) + + String extension = FileUtil.getExtension(fileName); + if (extension.isEmpty()) { + extension = "png"; // made up + } + + // now have to resize this file. + File newFile = new File(FileUtil.TEMP_DIR, "temp_resize." + extension).getAbsoluteFile(); + Image image; + + // is file sitting on drive + File iconTest = new File(fileName); + if (iconTest.isFile() && iconTest.canRead()) { + final String absolutePath = iconTest.getAbsolutePath(); + + // resize the image, keep aspect + image = new ImageIcon(absolutePath).getImage().getScaledInstance(sizeWidth, -1, Image.SCALE_SMOOTH); + image.flush(); + } + else { + // suck it out of a URL/Resource (with debugging if necessary) + final URL systemResource = LocationResolver.getResource(fileName); + + // resize the image, keep aspect + image = new ImageIcon(systemResource).getImage().getScaledInstance(sizeWidth, -1, Image.SCALE_SMOOTH); + image.flush(); + } + + // have to do this twice, so that it will finish loading the image (weird callback stuff is required if we don't do this) + image = new ImageIcon(image).getImage(); + image.flush(); + + // make whatever dirs we need to. + boolean mkdirs = newFile.getParentFile() + .mkdirs(); + + if (!mkdirs) { + throw new IOException("Unable to create directories for " + newFile.getParentFile()); + } + + // if it's already there, we have to delete it + boolean delete = newFile.delete(); + if (!delete) { + throw new IOException("Temporary file already in use, cannot delete it " + newFile); + } + + // now write out the new one + BufferedImage bufferedImage = getBufferedImage(image); + ImageIO.write(bufferedImage, extension, newFile); + + return newFile.getAbsolutePath(); + } + + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static + File getTransparentImage(final int size, final File fileToUse) throws IOException { + if (fileToUse.canRead() && fileToUse.isFile()) { + return fileToUse; + } + + // make sure the directory exists + fileToUse.getParentFile().mkdirs(); + + final BufferedImage image = getTransparentImageAsBufferedImage(size); + ImageIO.write(image, "png", fileToUse); + return fileToUse; + } + + @SuppressWarnings("WeakerAccess") + public static + BufferedImage getTransparentImageAsBufferedImage(final int size) { + final BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = image.createGraphics(); + g2d.setColor(new Color(0, 0, 0, 0)); + g2d.fillRect(0, 0, size, size); + g2d.dispose(); + + return image; + } + + + /** + * @return the image as a Buffered Image + */ + public static + BufferedImage getBufferedImage(Image image) { + if (image instanceof BufferedImage) { + return (BufferedImage) image; + } + + BufferedImage bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); + + Graphics2D bGr = bimage.createGraphics(); + bGr.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY)); + bGr.drawImage(image, 0, 0, null); + bGr.dispose(); + + // Return the buffered image + return bimage; + } + + + /** + * Reads the image size information from the specified file, without loading the entire file. + * + * @param fileStream the input stream of the file + * + * @return the image size dimensions. IOException if it could not be read + */ + public static + Dimension getImageSize(InputStream fileStream) throws IOException { + ImageInputStream in = null; + ImageReader reader = null; + try { + // This will ONLY work for File, InputStream, and RandomAccessFile + in = ImageIO.createImageInputStream(fileStream); + + final Iterator readers = ImageIO.getImageReaders(in); + if (readers.hasNext()) { + reader = readers.next(); + reader.setInput(in); + + return new Dimension(reader.getWidth(0), reader.getHeight(0)); + } + } finally { + // `ImageInputStream` is not a closeable in 1.6, so we do this manually. + if (in != null) { + try { + in.close(); + } catch (IOException ignored) { + } + } + + if (reader != null) { + reader.dispose(); + } + } + + throw new IOException("Unable to read file inputStream for image size data."); + } + + /** + * Because of the way image loading works in Java, if one wants to IMMEDIATELY get a fully loaded image, one must resort to "hacks" + * by loading the image twice. + * + * @param image the image you want load immediately + * + * @return a fully loaded image + */ + public static + Image getImageImmediate(final Image image) { + // have to do this twice, so that it will finish loading the image (weird callback stuff is required if we don't do this) + image.flush(); + + final Image loadedImage = new ImageIcon(image).getImage(); + loadedImage.flush(); + + return loadedImage; + } +}