Used by the image optimization service to indicate if and how images
@@ -129,7 +137,7 @@ public final static boolean isEnabled(final FileTypeConversion fileTypeConversio
* The complete list of supported file extensions that the service will
* optimize.
*/
- public final static String[] SUPPORTED_FILE_EXTENSIONS = {PNG_EXTENSION, JPEG_EXTENSION, GIF_EXTENSION, JPEG_EXTENSION2, JPEG_EXTENSION3, PNG_EXTENSION.toUpperCase(), JPEG_EXTENSION.toUpperCase(), GIF_EXTENSION.toUpperCase(), JPEG_EXTENSION2.toUpperCase(), JPEG_EXTENSION3.toUpperCase()};
+ public final static String[] SUPPORTED_FILE_EXTENSIONS = {PNG_EXTENSION, JPEG_EXTENSION, GIF_EXTENSION, JPEG_EXTENSION2, JPEG_EXTENSION3, SVG_EXTENSION, PNG_EXTENSION.toUpperCase(), JPEG_EXTENSION.toUpperCase(), GIF_EXTENSION.toUpperCase(), JPEG_EXTENSION2.toUpperCase(), JPEG_EXTENSION3.toUpperCase(), SVG_EXTENSION.toUpperCase()};
/**
* This method will try to optimize all of the passed in images.
diff --git a/src/com/salesforce/perfeng/uiperf/imageoptimization/service/ImageOptimizationService.java b/src/main/java/com/salesforce/perfeng/uiperf/imageoptimization/service/ImageOptimizationService.java
similarity index 86%
rename from src/com/salesforce/perfeng/uiperf/imageoptimization/service/ImageOptimizationService.java
rename to src/main/java/com/salesforce/perfeng/uiperf/imageoptimization/service/ImageOptimizationService.java
index 7b61f10..67e7a50 100644
--- a/src/com/salesforce/perfeng/uiperf/imageoptimization/service/ImageOptimizationService.java
+++ b/src/main/java/com/salesforce/perfeng/uiperf/imageoptimization/service/ImageOptimizationService.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014, Salesforce.com, Inc.
+ * Copyright (c) 2017, Salesforce.com, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,6 +31,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
+import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
@@ -56,8 +57,6 @@
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
-import org.apache.http.annotation.Immutable;
-import org.apache.http.annotation.ThreadSafe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,16 +65,15 @@
import com.salesforce.perfeng.uiperf.imageoptimization.utils.FixedFileUtils;
import com.salesforce.perfeng.uiperf.imageoptimization.utils.ImageFileOptimizationException;
import com.salesforce.perfeng.uiperf.imageoptimization.utils.ImageUtils;
+import com.salesforce.perfeng.uiperf.imageoptimization.utils.ImagesEqual;
/**
- * Service used to perform the optimization of images.
+ * Service used to perform the optimization of images. This class is Immutable and ThreadSafe.
*
* @author eperret (Eric Perret)
* @since 186.internal
* @param BufferedImage
*/
@SuppressWarnings("unused")
- final static BufferedImage getBufferedImage(final File file) {
+ final static BufferedImage getBufferedImage(File file) {
+
Image image;
-
- try (final FileInputStream inputStream = new FileInputStream(file)) {
+
+ try {
// ImageIO.read(file) is broken for some images so I went this
// route
image = Toolkit.getDefaultToolkit().createImage(file.getCanonicalPath());
@@ -116,60 +90,59 @@ final static BufferedImage getBufferedImage(final File file) {
} catch(final Exception e2) {
throw new ImageFileOptimizationException(file.getPath(), e2);
}
-
- final BufferedImage converted = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
- final Graphics2D g2d = converted.createGraphics();
- g2d.drawImage(image, 0, 0, null);
- g2d.dispose();
+
+ final BufferedImage converted;
+ if((image != null) && (image.getWidth(null) > 0) && (image.getHeight(null) > 0)) {
+ converted = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
+ final Graphics2D g2d = converted.createGraphics();
+ g2d.drawImage(image, 0, 0, null);
+ g2d.dispose();
+ } else {
+ try {
+ converted = ImageIO.read(file);
+ } catch(final Exception e2) {
+ throw new ImageFileOptimizationException(file.getPath(), e2);
+ }
+ }
return converted;
}
/**
- * Compares file1 to file2 to see if they are the same based on a visual
- * pixel by pixel comparison. This has issues with marking images different
- * when they are not. Works perfectly for all images.
- *
- * @param file1 First file to compare
- * @param file2 Second image to compare
- * @return true if they are equal, otherwise
- * false.
- */
- private final static boolean visuallyCompareJava(final File file1, final File file2) {
- return equals(getPixels(getBufferedImage(file1), file1), getPixels(getBufferedImage(file2), file2));
- }
-
- /**
- * Compares file1 to file2 to see if they are the same based on a visual
- * pixel by pixel comparison. This has issues with marking images different
- * when they are not. Works perfectly for all images.
+ * Gets the array of pixels from the passed in {@link BufferedImage}.
*
- * @param file1 Image 1 to compare
- * @param file2 Image 2 to compare
- * @return true if both images are visually the same.
+ * @param img The image to get the pixels from
+ * @param file The file containing the image
+ * @return An array containing the pixels
*/
- public final static boolean visuallyCompare(final File file1, final File file2) {
-
- logger.debug("Start comparing \"{}\" and \"{}\".", file1.getPath(), file2.getPath());
+ static final int[] getPixels(final BufferedImage img, final File file) {
- if(file1 == file2) {
- return true;
- }
-
- boolean answer = visuallyCompareJava(file1, file2);
-
- if(!answer) {
- logger.info("The files \"{}\" and \"{}\" are not pixel by pixel the same image. Manual comparison required.", file1.getPath(), file2.getPath());
+ final int width = img.getWidth();
+ final int height = img.getHeight();
+ int[] pixelData = new int[width * height];
+
+ final Image pixelImg;
+ if (img.getColorModel().getColorSpace() == ColorSpace.getInstance(ColorSpace.CS_sRGB)) {
+ pixelImg = img;
+ } else {
+ pixelImg = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null).filter(img, null);
+ }
+
+ final PixelGrabber pg = new PixelGrabber(pixelImg, 0, 0, width, height, pixelData, 0, width);
+
+ try {
+ if(!pg.grabPixels()) {
+ throw new RuntimeException();
+ }
+ } catch (final InterruptedException ie) {
+ throw new ImageFileOptimizationException(file.getPath(), ie);
}
- logger.debug("Finish comparing \"{}\" and \"{}\".", file1.getPath(), file2.getPath());
-
- return answer;
+ return pixelData;
}
/**
* @param file The image to check
- * @return true if the image contains one or more pixels with
- * some percentage of transparency (Alpha)
+ * @return {@code true} if the image contains one or more pixels with some percentage of transparency (Alpha)
*/
public final static boolean containsAlphaTransparency(final File file) {
logger.debug("Start Alpha pixel check for {}.", file.getPath());
@@ -196,7 +169,7 @@ private final static void handleOptimizationFailure(final Process ps, final Stri
try(final StringWriter writer = new StringWriter();
final InputStream is = ps.getInputStream()) {
try {
- IOUtils.copy(is, writer);
+ IOUtils.copy(is, writer, Charset.defaultCharset());
final StringBuilder errorMessage = new StringBuilder("Image conversion failed with edit code: ").append(ps.exitValue()).append(". ").append(writer);
if(ps.exitValue() == 127 /* command not found */) {
throw new ThirdPartyBinaryNotFoundException(binaryApplicationName, "Most likely this is due to ImageMagick not being installed on the OS. On Ubuntu run \"sudo apt-get install imagemagick\".", new RuntimeException(errorMessage.toString()));
@@ -231,7 +204,7 @@ public final static void convertImageNative(final File fromImage, final File toI
try {
ps = new ProcessBuilder(CONVERT_BINARY, fromImage.getCanonicalPath(), toImage.getCanonicalPath()).start();
} catch(final IOException ioe) {
- throw new ThirdPartyBinaryNotFoundException(CONVERT_BINARY, "Most likely this is due to ImageMagic not being installed on the OS. On Ubuntu run \"sudo apt-get install imagemagick\".", ioe);
+ throw new ThirdPartyBinaryNotFoundException(CONVERT_BINARY, "Most likely this is due to ImageMagick not being installed on the OS. On Ubuntu run, \"sudo apt-get install imagemagick\".", ioe);
}
if((ps.waitFor() != 0) || !toImage.exists()) {
diff --git a/src/main/java/com/salesforce/perfeng/uiperf/imageoptimization/utils/ImagesEqual.java b/src/main/java/com/salesforce/perfeng/uiperf/imageoptimization/utils/ImagesEqual.java
new file mode 100644
index 0000000..34cd027
--- /dev/null
+++ b/src/main/java/com/salesforce/perfeng/uiperf/imageoptimization/utils/ImagesEqual.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * Copyright (c) 2017, Salesforce.com, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of Salesforce.com nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+package com.salesforce.perfeng.uiperf.imageoptimization.utils;
+
+import static com.salesforce.perfeng.uiperf.imageoptimization.utils.ImageUtils.getBufferedImage;
+import static com.salesforce.perfeng.uiperf.imageoptimization.utils.ImageUtils.getPixels;
+
+import java.io.File;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author eperret (Eric Perret)
+ *
+ */
+public final class ImagesEqual {
+
+ private final static Logger logger = LoggerFactory.getLogger(ImagesEqual.class);
+
+ private static final boolean equals(final int[] data1, final int[] data2) {
+ final int length = data1.length;
+ if (length != data2.length) {
+ logger.debug("File lengths are different.");
+ return false;
+ }
+ for(int i = 0; i < length; i++) {
+ if(data1[i] != data2[i]) {
+
+ //If the alpha is 0 for both that means that the pixels are 100%
+ //transparent and the color does not matter. Return false if
+ //only 1 is 100% transparent.
+ if((((data1[i] >> 24) & 0xff) == 0) && (((data2[i] >> 24) & 0xff) == 0)) {
+ logger.debug("Both pixles at spot {} are different but 100% transparent.", Integer.valueOf(i));
+ } else {
+ logger.debug("The pixel {} is different.", Integer.valueOf(i));
+ return false;
+ }
+ }
+ }
+ logger.debug("Both groups of pixels are the same.");
+ return true;
+ }
+
+ /**
+ * Compares file1 to file2 to see if they are the same based on a visual
+ * pixel by pixel comparison. This has issues with marking images different
+ * when they are not. Works perfectly for all images.
+ *
+ * @param file1 First file to compare
+ * @param file2 Second image to compare
+ * @return true if they are equal, otherwise
+ * false.
+ */
+ private final static boolean visuallyCompareJava(final File file1, final File file2) {
+ return equals(getPixels(getBufferedImage(file1), file1), getPixels(getBufferedImage(file2), file2));
+ }
+
+ /**
+ * Compares file1 to file2 to see if they are the same based on a visual
+ * pixel by pixel comparison. This has issues with marking images different
+ * when they are not. Works perfectly for all images.
+ *
+ * @param file1 Image 1 to compare
+ * @param file2 Image 2 to compare
+ * @return true if both images are visually the same.
+ */
+ public final static boolean visuallyCompare(final File file1, final File file2) {
+
+ if(file1 == null || file2 == null) {
+ throw new IllegalArgumentException("The passed in files cannot be null.");
+ }
+ if(!file1.canRead()) {
+ throw new IllegalArgumentException("The passed in file, \"" + file1.getPath() + "\", cannot be read or does not exist.");
+ }
+ if(!file2.canRead()) {
+ throw new IllegalArgumentException("The passed in file, \"" + file2.getPath() + "\", cannot be read or does not exist.");
+ }
+
+ logger.debug("Start comparing \"{}\" and \"{}\".", file1.getPath(), file2.getPath());
+
+ if(file1 == file2) {
+ return true;
+ }
+
+ boolean answer = visuallyCompareJava(file1, file2);
+
+ if(!answer) {
+ logger.info("The files \"{}\" and \"{}\" are not pixel by pixel the same image. Manual comparison required.", file1.getPath(), file2.getPath());
+ }
+
+ logger.debug("Finish comparing \"{}\" and \"{}\".", file1.getPath(), file2.getPath());
+
+ return answer;
+ }
+}
diff --git a/src/main/java/log4j2.xml b/src/main/java/log4j2.xml
new file mode 100644
index 0000000..6f59763
--- /dev/null
+++ b/src/main/java/log4j2.xml
@@ -0,0 +1,18 @@
+