SystemTray/src/dorkbox/systemTray/util/ImageResizeUtil.java
2017-06-26 14:07:45 +02:00

383 lines
13 KiB
Java

/*
* 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.systemTray.util;
import static dorkbox.util.ImageUtil.getBufferedImage;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import dorkbox.systemTray.SystemTray;
import dorkbox.util.CacheUtil;
import dorkbox.util.IO;
import dorkbox.util.ImageUtil;
public
class ImageResizeUtil {
public static
File getTransparentImage() {
// here, it doesn't matter what size the image is, as long as there is an image, the text in the menu will be shifted correctly
// it is HIGHLY unlikely that the menu entry will be smaller than 4px.
return getTransparentImage(4);
}
public static
File getTransparentImage(final int imageSize) {
// NOTE: this does not need to be called on the EDT
try {
final File newFile = CacheUtil.create(imageSize + "_empty.png");
return ImageUtil.getTransparentImage(imageSize, newFile);
} catch (IOException e) {
throw new RuntimeException("Unable to generate transparent image! Something is severely wrong!");
}
}
private static
File getErrorImage(int size) {
if (size == 0) {
// default size
size = 32;
}
try {
InputStream imageStream = ImageResizeUtil.class.getResource("error_32.png").openStream();
// have to resize the image to be whatever size we specify
imageStream = makeByteArrayInputStream(imageStream);
imageStream.mark(0);
// check if we already have this file information saved to disk, based on size + hash of data
final String cacheName = size + "_" + CacheUtil.createNameAsHash(imageStream);
((ByteArrayInputStream) imageStream).reset(); // casting to avoid unnecessary try/catch for IOException
// if we already have this fileName, reuse it
final File check = CacheUtil.check(cacheName);
if (check != null) {
return check;
}
// we have to hop through hoops.
File resizedFile = resizeFileNoCheck(size, imageStream);
// now cache that file
File save = CacheUtil.save(cacheName, resizedFile);
return save;
} catch (Exception e) {
// this must be thrown
throw new RuntimeException("Serious problems! Unable to extract error image, this should NEVER happen!", e);
}
}
private static synchronized
File resizeAndCache(final int size, final File file) {
return resizeAndCache(size, file.getAbsolutePath());
}
private static synchronized
File resizeAndCache(final int size, final String fileName) {
if (fileName == null) {
return null;
}
try {
FileInputStream fileInputStream = new FileInputStream(fileName);
File file = resizeAndCache(size, fileInputStream);
fileInputStream.close();
return file;
} catch (Exception e) {
// have to serve up the error image instead.
SystemTray.logger.error("Error reading image. Using error icon instead", e);
return getErrorImage(size);
}
}
@SuppressWarnings("Duplicates")
private static synchronized
File resizeAndCache(final int size, InputStream imageStream) {
if (imageStream == null) {
return null;
}
final String cacheName;
// no cached file, so we resize then save the new one.
boolean needsResize = true;
try {
imageStream = makeByteArrayInputStream(imageStream);
imageStream.mark(0);
// check if we already have this file information saved to disk, based on size + hash of data
cacheName = size + "_" + CacheUtil.createNameAsHash(imageStream);
((ByteArrayInputStream) imageStream).reset(); // casting to avoid unnecessary try/catch for IOException
// if we already have this fileName, reuse it
final File check = CacheUtil.check(cacheName);
if (check != null) {
return check;
}
imageStream.mark(0);
Dimension imageSize = ImageUtil.getImageSize(imageStream);
//noinspection NumericCastThatLosesPrecision
if (size == ((int) imageSize.getWidth()) && size == (int) imageSize.getHeight()) {
// we can reuse this URL (it's the correct size).
needsResize = false;
}
} catch (Exception e) {
// have to serve up the error image instead.
SystemTray.logger.error("Error getting image size. Using error icon instead", e);
return getErrorImage(size);
} finally {
((ByteArrayInputStream) imageStream).reset(); // casting to avoid unnecessary try/catch for IOException
}
if (needsResize) {
// we have to hop through hoops.
try {
File resizedFile = resizeFileNoCheck(size, imageStream);
// now cache that file
try {
return CacheUtil.save(cacheName, resizedFile);
} catch (Exception e) {
// have to serve up the error image instead.
SystemTray.logger.error("Error caching image. Using error icon instead", e);
return getErrorImage(size);
}
} catch (Exception e) {
// have to serve up the error image instead.
SystemTray.logger.error("Error resizing image. Using error icon instead", e);
return getErrorImage(size);
}
} else {
// no resize necessary, just cache as is.
try {
return CacheUtil.save(cacheName, imageStream);
} catch (Exception e) {
// have to serve up the error image instead.
SystemTray.logger.error("Error caching image. Using error icon instead", e);
return getErrorImage(size);
}
}
}
// if this input stream is NOT a ByteArrayInputStream, make it one.
private static
InputStream makeByteArrayInputStream(InputStream imageStream) throws IOException {
if (!(imageStream instanceof ByteArrayInputStream)) {
// have to make a copy of the inputStream, but only if necessary
ByteArrayOutputStream byteArrayOutputStream = IO.copyStream(imageStream);
imageStream.close();
imageStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
}
return imageStream;
}
/**
* Resizes the given InputStream to the specified size. No checks are performed if it's the correct size to begin with.
*
* @return the file on disk that is the resized icon
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
private static
File resizeFileNoCheck(final int size, InputStream inputStream) throws IOException {
// have to resize the file (and return the new path)
// now have to resize this file.
File newFile = CacheUtil.create("temp_resize.png");
Image image;
// resize the image, keep aspect
image = ImageUtil.getImageImmediate(ImageIO.read(inputStream));
image = image.getScaledInstance(size, -1, Image.SCALE_SMOOTH);
// if it's already there, we have to delete it
newFile.delete();
// now write out the new one
BufferedImage bufferedImage = getBufferedImage(image);
ImageIO.write(bufferedImage, "png", newFile); // made up extension
return newFile;
}
public static
File shouldResizeOrCache(final boolean isTrayImage, final File imageFile) {
if (imageFile == null) {
return null;
}
if (SystemTray.AUTO_SIZE) {
return ImageResizeUtil.resizeAndCache(getSize(isTrayImage), imageFile);
} else {
return imageFile;
}
}
public static
File shouldResizeOrCache(final boolean isTrayImage, final String imagePath) {
if (imagePath == null) {
return null;
}
if (SystemTray.AUTO_SIZE) {
return ImageResizeUtil.resizeAndCache(getSize(isTrayImage), imagePath);
} else {
return new File(imagePath);
}
}
public static
File shouldResizeOrCache(final boolean isTrayImage, final URL imageUrl) {
if (imageUrl == null) {
return null;
}
try {
if (SystemTray.AUTO_SIZE) {
InputStream inputStream = imageUrl.openStream();
File file = resizeAndCache(getSize(isTrayImage), inputStream);
inputStream.close();
return file;
} else {
return CacheUtil.save(imageUrl);
}
} catch (Exception e) {
// have to serve up the error image instead.
SystemTray.logger.error("Error reading image. Using error icon instead", e);
return getErrorImage(getSize(isTrayImage));
}
}
public static
File shouldResizeOrCache(final boolean isTrayImage, final InputStream imageStream) {
if (imageStream == null) {
return null;
}
if (SystemTray.AUTO_SIZE) {
return ImageResizeUtil.resizeAndCache(getSize(isTrayImage), imageStream);
} else {
try {
return CacheUtil.save(imageStream);
} catch (IOException e) {
SystemTray.logger.error("Error checking cache for information. Using error icon instead", e);
return getErrorImage(0);
}
}
}
public static
File shouldResizeOrCache(final boolean isTrayImage, final Image image) {
if (image == null) {
return null;
}
try {
final Image trayImage = ImageUtil.getImageImmediate(image);
BufferedImage bufferedImage = getBufferedImage(trayImage);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "png", os);
InputStream imageInputStream = new ByteArrayInputStream(os.toByteArray());
if (SystemTray.AUTO_SIZE) {
File file = resizeAndCache(getSize(isTrayImage), imageInputStream);
imageInputStream.close(); // BAOS doesn't do anything, but here for completeness + documentation
return file;
} else {
File file = CacheUtil.save(imageInputStream);
imageInputStream.close(); // BAOS doesn't do anything, but here for completeness + documentation
return file;
}
} catch (Exception e) {
// have to serve up the error image instead.
SystemTray.logger.error("Error reading image. Using error icon instead", e);
return getErrorImage(getSize(isTrayImage));
}
}
public static
File shouldResizeOrCache(final boolean isTrayImage, final ImageInputStream imageStream) {
if (imageStream == null) {
return null;
}
try {
ByteArrayOutputStream byteArrayOutputStream = IO.copyStream(imageStream);
ByteArrayInputStream fileStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
if (SystemTray.AUTO_SIZE) {
return resizeAndCache(getSize(isTrayImage), fileStream);
} else {
return CacheUtil.save(fileStream);
}
} catch (Exception e) {
// have to serve up the error image instead.
SystemTray.logger.error("Error reading image. Using error icon instead", e);
return getErrorImage(getSize(isTrayImage));
}
}
private static
int getSize(final boolean isTrayImage) {
int size;
if (isTrayImage) {
// system tray image
size = SizeAndScalingUtil.TRAY_SIZE;
} else {
// menu image
size = SizeAndScalingUtil.TRAY_MENU_SIZE;
}
return size;
}
}