
fiftyone.mobile.detection.webapp.ImageOptimizer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of 51Degrees.detection.webapp Show documentation
Show all versions of 51Degrees.detection.webapp Show documentation
51Degrees's WebApp library to be used with the core solution for extra functionality
The newest version!
package fiftyone.mobile.detection.webapp;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/* *********************************************************************
* This Source Code Form is copyright of 51Degrees Mobile Experts Limited.
* Copyright 2014 51Degrees Mobile Experts Limited, 5 Charlotte Close,
* Caversham, Reading, Berkshire, United Kingdom RG4 7BY
*
* This Source Code Form is the subject of the following patent
* applications, owned by 51Degrees Mobile Experts Limited of 5 Charlotte
* Close, Caversham, Reading, Berkshire, United Kingdom RG4 7BY:
* European Patent Application No. 13192291.6; and
* United States Patent Application Nos. 14/085,223 and 14/085,301.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0.
*
* If a copy of the MPL was not distributed with this file, You can obtain
* one at http://mozilla.org/MPL/2.0/.
*
* This Source Code Form is "Incompatible With Secondary Licenses", as
* defined by the Mozilla Public License, v. 2.0.
* ********************************************************************* */
class ImageOptimizer {
private final static Logger logger = LoggerFactory
.getLogger(ImageOptimizer.class);
private static final String IMAGE_MAX_WIDTH = "IMAGE_MAX_WIDTH";
private static final String IMAGE_MAX_HEIGHT = "IMAGE_MAX_HEIGHT";
private static final String IMAGE_FACTOR = "IMAGE_FACTOR";
private static final String IMAGE_WIDTH_PARAM = "IMAGE_WIDTH_PARAM";
private static final String IMAGE_HEIGHT_PARAM = "IMAGE_HEIGHT_PARAM";
private static final String IMAGE_DEFAULT_AUTO = "IMAGE_DEFAULT_AUTO";
private static final String EMPTY_IMAGE_RESOURCE_NAME = "E.gif";
private static final int DEFAULT_BUFFER_SIZE = 10240;
private static final String AUTO_STRING = "auto";
private static final String SCREEN_PIXEL_WIDTH = "ScreenPixelsWidth";
private static final String SCREEN_PIXEL_HEIGHT = "ScreenPixelsHeight";
/**
* Get a list of parameters from with the query string.
* @param request
* @return
*/
private static Map getQueryString(HttpServletRequest request) {
Map parameters = new HashMap();
List params = new ArrayList();
if (request.getQueryString() != null) {
StringTokenizer ps = new StringTokenizer(
request.getQueryString(), "&", false);
while (ps.hasMoreTokens()) {
params.add(ps.nextToken());
}
for (String param : params) {
StringTokenizer st = new StringTokenizer(param, "=", false);
if (st.countTokens() == 2) {
parameters.put(st.nextToken(), st.nextToken());
}
}
}
return parameters;
}
private static Size getRequiredSize(HttpServletRequest request) {
int width = 0;
int height = 0;
Map parameters = getQueryString(request);
String widthParam = getWidthParam(request);
String heightParam = getHeightParam(request);
if (parameters.containsKey(getWidthParam(request))) {
try {
width = Integer.parseInt(parameters.get(widthParam));
}
catch (NumberFormatException ex) {
if (AUTO_STRING.equalsIgnoreCase(parameters.get(widthParam))) {
width = getDefaultAuto(request);
}
else {
width = getImageMaxWidth(request);
}
}
}
if (parameters.containsKey(heightParam)) {
try {
height = Integer.parseInt(parameters.get(heightParam));
}
catch (NumberFormatException ex) {
if (AUTO_STRING.equalsIgnoreCase(parameters.get(heightParam))) {
height = getDefaultAuto(request);
}
else {
height = getImageMaxHeight(request);
}
}
}
return new Size(width, height);
}
/**
* Adjust the height of the image so that it is not larger than the maximum
* allowed height.
* @param Size
*/
private static void resolveHeight(int imageMaxHeight, Size size)
{
if (size.height > imageMaxHeight)
{
double ratio = (double)imageMaxHeight / (double)size.height;
size.height = imageMaxHeight;
size.width = (int)((double)size.height * ratio);
}
}
/**
* Adjust the width of the image so that it is not larger than the maximum
* allowed width.
* @param Size
*/
private static void resolveWidth(int imageMaxWidth , Size size)
{
if (size.width > imageMaxWidth)
{
double ratio = (double)imageMaxWidth / (double)size.width;
size.width = imageMaxWidth;
size.height = (int)((double)size.height * ratio);
}
}
private static void resolveSize(HttpServletRequest request, Size size) {
int imageMaxWidth = getImageMaxWidth(request);
int imageMaxHeight = getImageMaxHeight(request);
// Check that a size has been requested.
if (size.width > 0 || size.height > 0)
{
// Check that no dimensions are above the specifed max.
if (size.height > imageMaxHeight ||
size.width > imageMaxWidth)
{
if (size.height > size.width)
{
resolveWidth(imageMaxWidth, size);
resolveHeight(imageMaxHeight, size);
}
else
{
resolveHeight(imageMaxHeight, size);
resolveWidth(imageMaxWidth, size);
}
}
// Use the factor to adjust the size.
int factor = getFactor(request);
size.height = factor * (int)Math.floor((double)size.height / factor);
size.width = factor * (int)Math.floor((double)size.width / factor);
// If the image is 0x0 after factoring then set the width to
// the factor so that a little image gets returned.
if (size.height == 0 && size.width == 0) {
size.width = factor;
}
}
}
/**
* Sends the requested image, at the requested size as the response.
* @param request
* @param response
* @throws IOException
*/
static void sendImage(HttpServletRequest request, HttpServletResponse response)
throws IOException {
// Check to see if this is for an empty image.
if ("/Empty.gif".equalsIgnoreCase(request.getPathInfo())) {
sendEmpty(response);
return;
}
File sourceFile = new File(request.getServletContext().getRealPath(
request.getPathInfo()));
// Check to see if the source file exists.
if (sourceFile.exists() == false) {
response.sendError(404);
return;
}
Size size = getRequiredSize(request);
String cacheDirectory = String.format(
"%s/cache",
request.getServletContext().getRealPath("WEB-INF"));
if (size.width == 0 && size.height == 0) {
Map results = WebProvider.getResult(request);
// Get the screen width and height.
try {
if (results.get(SCREEN_PIXEL_WIDTH) != null &&
results.get(SCREEN_PIXEL_WIDTH).length > 0) {
size.width = Integer.parseInt(results.get(SCREEN_PIXEL_WIDTH)[0]);
}
}
catch (NumberFormatException ex) {
size.width = 0;
}
try {
if (results.get(SCREEN_PIXEL_HEIGHT) != null &&
results.get(SCREEN_PIXEL_HEIGHT).length > 0) {
size.height = Integer.parseInt(results.get(SCREEN_PIXEL_HEIGHT)[0]);
}
}
catch (NumberFormatException ex) {
size.height = 0;
}
// Use the larger of the two values as the width as there is no
// way of knowing if the device is in landscape or portrait
// orientation.
size.width = Math.max(size.width, size.height);
size.height = 0;
}
// Ensure the size is not larger than the maximum values.
resolveSize(request, size);
if (size.width > 0 || size.height > 0)
{
// Get the files and paths involved in the caching.
File cachedFile = ImageCache.lookup(
cacheDirectory, sourceFile, size.width, size.height);
logger.debug(String.format(
"Image processor is responding with image '%s' of width '%d' and height '%d'",
sourceFile,
size.width,
size.height));
// If the cached file doesn't exist or is out of date
// then create a new cached file and serve this in response
// to the request by rewriting the requested URL to the
// static file.
if ((cachedFile.exists() == false && cachedFile.isFile()) ||
cachedFile.lastModified() < sourceFile.lastModified())
{
// Shrink the image and store in the cache.
BufferedImage image = createBufferedImage(
size.width,
size.height,
new FileInputStream(sourceFile));
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image,
"image/jpeg".equals(request.getServletContext().getMimeType(
sourceFile.getAbsolutePath()))
? "jpg" : "png", os);
ByteArrayInputStream imageAsStream = new ByteArrayInputStream(os.toByteArray());
ImageCache.add(sourceFile, cachedFile, size.width, size.height, imageAsStream);
imageAsStream.close();
}
// Send the response from the cached file.
File responseFile = cachedFile.exists() ? cachedFile : sourceFile;
FileInputStream responseStream = new FileInputStream(responseFile);
try {
sendResponse(response, responseStream, responseFile.length());
}
finally {
responseStream.close();
}
}
else {
// No size has been calculated for the image so return the original
// one.
FileInputStream responseStream = new FileInputStream(sourceFile);
try {
sendResponse(response, responseStream, sourceFile.length());
}
finally {
responseStream.close();
}
}
}
private static void sendResponse(
HttpServletResponse response,
InputStream imageResponse,
long imageLength) throws IOException {
response.reset();
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setHeader("Content-Length",
String.valueOf(imageLength));
BufferedOutputStream output = null;
try {
output = new BufferedOutputStream(
response.getOutputStream(),
DEFAULT_BUFFER_SIZE);
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int length;
while ((length = imageResponse.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
} finally {
close(output);
}
}
private static int getDefaultAuto(HttpServletRequest request) {
return getIntegerParameter(
request,
Constants.DEFAULT_AUTO,
50);
}
private static int getFactor(HttpServletRequest request) {
return getIntegerParameter(
request,
Constants.IMAGE_FACTOR,
1);
}
private static int getImageMaxHeight(HttpServletRequest request) {
return getIntegerParameter(
request,
Constants.IMAGE_MAX_HEIGHT,
Integer.MAX_VALUE);
}
private static int getImageMaxWidth(HttpServletRequest request) {
return getIntegerParameter(
request,
Constants.IMAGE_MAX_WIDTH,
Integer.MAX_VALUE);
}
private static String getHeightParam(HttpServletRequest request) {
return getStringParameter(
request,
Constants.IMAGE_HEIGHT_PARAM,
"h");
}
private static String getWidthParam(HttpServletRequest request) {
return getStringParameter(
request,
Constants.IMAGE_WIDTH_PARAM,
"w");
}
/**
* Creates a buffered image based on the image stream provided as input.
* @param width
* @param height
* @param imageAsStream
* @return
* @throws IOException
*/
private static BufferedImage createBufferedImage(int width, int height,
InputStream imageAsStream) throws IOException {
BufferedImage original = ImageIO.read(imageAsStream);
int originalWidth = original.getWidth();
int originalHeight = original.getHeight();
double widthRatio = (double) width / (double) originalWidth;
double heightRatio = (double) height / (double) originalHeight;
// Calculate the size of the shrunk image.
if (widthRatio > 0 && heightRatio > 0 && widthRatio <= 1
&& heightRatio <= 1) {
width = (int) ((double) originalWidth * widthRatio);
height = (int) ((double) originalHeight * heightRatio);
} else if (widthRatio > 0 && heightRatio == 0 && widthRatio <= 1) {
width = (int) ((double) originalWidth * widthRatio);
height = (int) ((double) originalHeight * widthRatio);
} else if (widthRatio == 0 && heightRatio > 0 && heightRatio <= 1) {
width = (int) ((double) originalWidth * heightRatio);
height = (int) ((double) originalHeight * heightRatio);
} else {
// No change so return the original.
return original;
}
// Check for zero values dimensions to avoid exceptions in Java.
if (width == 0) {
width = 1;
}
if (height == 0) {
height = 1;
}
BufferedImage resized = new BufferedImage(
width,
height,
original.getType());
resized.getGraphics().drawImage(
original.getScaledInstance(
width,
height,
BufferedImage.SCALE_SMOOTH), 0, 0, null);
return resized;
}
private static void close(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException ex) {
logger.debug(
"Exception closing resource",
ex);
}
}
}
private static void sendEmpty(HttpServletResponse response)
throws IOException {
InputStream empty = ImageOptimizer.class.getResourceAsStream(
EMPTY_IMAGE_RESOURCE_NAME);
int length = 0;
empty.mark(Integer.MAX_VALUE);
while(empty.read() >= 0) {
length++;
}
empty.reset();
response.setContentType("image/gif");
sendResponse(response, empty, length);
}
/**
* Returns and string representation of the configuaration parameter
* requested. If the parameter does not exist then the default value
* is returned.
* @param servletContext
* @param parameterName
* @param defaultValue
* @return
*/
private static String getStringParameter(
final HttpServletRequest request,
final String parameterName,
final String defaultValue) {
String parameter = request.getServletContext().getInitParameter(parameterName);
return parameter == null ? defaultValue : parameter;
}
/**
* Returns and integer representation of the configuaration parameter
* requested. If the parameter does not exist then the default value
* is returned.
* @param servletContext
* @param parameterName
* @param defaultValue
* @return
*/
private static int getIntegerParameter(
final HttpServletRequest request,
final String parameterName,
final int defaultValue) {
String parameter = request.getServletContext().getInitParameter(parameterName);
if (parameter != null) {
try {
return Integer.parseInt(parameter);
} catch (NumberFormatException ex) {
// No value, nothing to do.
}
}
return defaultValue;
}
/**
* Returns the image optimisation JavaScript for the current request.
* @param request
* @return
* @throws IOException
*/
static String getJavascript(HttpServletRequest request) throws IOException {
Map results = WebProvider.getResult(request);
if (results != null) {
String[] values = results.get("JavascriptImageOptimiser");
if (values != null &&
values.length == 1) {
return values[0];
}
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy