VIVO-848 move the image processing code behind an interface
This commit is contained in:
parent
c1bb928096
commit
c751ecdc6d
9 changed files with 266 additions and 161 deletions
|
@ -6,7 +6,9 @@ import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletContextEvent;
|
import javax.servlet.ServletContextEvent;
|
||||||
import javax.servlet.ServletContextListener;
|
import javax.servlet.ServletContextListener;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessor;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
||||||
import edu.cornell.mannlib.vitro.webapp.searchengine.SearchEngineWrapper;
|
import edu.cornell.mannlib.vitro.webapp.searchengine.SearchEngineWrapper;
|
||||||
import edu.cornell.mannlib.vitro.webapp.searchengine.solr.SolrSearchEngine;
|
import edu.cornell.mannlib.vitro.webapp.searchengine.solr.SolrSearchEngine;
|
||||||
|
@ -22,6 +24,7 @@ public class ApplicationImpl implements Application {
|
||||||
|
|
||||||
private final ServletContext ctx;
|
private final ServletContext ctx;
|
||||||
private SearchEngine searchEngine;
|
private SearchEngine searchEngine;
|
||||||
|
private ImageProcessor imageProcessor;
|
||||||
|
|
||||||
public ApplicationImpl(ServletContext ctx) {
|
public ApplicationImpl(ServletContext ctx) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
|
@ -41,10 +44,18 @@ public class ApplicationImpl implements Application {
|
||||||
this.searchEngine = searchEngine;
|
this.searchEngine = searchEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImageProcessor getImageProcessor() {
|
||||||
|
return imageProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageProcessor(ImageProcessor imageProcessor) {
|
||||||
|
this.imageProcessor = imageProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// The Setup class.
|
// The Setup class.
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
public static class Setup implements ServletContextListener {
|
public static class Setup implements ServletContextListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,11 +64,15 @@ public class ApplicationImpl implements Application {
|
||||||
StartupStatus ss = StartupStatus.getBean(ctx);
|
StartupStatus ss = StartupStatus.getBean(ctx);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
ApplicationImpl application = new ApplicationImpl(ctx);
|
||||||
|
|
||||||
SearchEngine searchEngine = new SearchEngineWrapper(
|
SearchEngine searchEngine = new SearchEngineWrapper(
|
||||||
new SolrSearchEngine());
|
new SolrSearchEngine());
|
||||||
|
|
||||||
ApplicationImpl application = new ApplicationImpl(ctx);
|
|
||||||
application.setSearchEngine(searchEngine);
|
application.setSearchEngine(searchEngine);
|
||||||
|
|
||||||
|
ImageProcessor imageProcessor = new JaiImageProcessor();
|
||||||
|
application.setImageProcessor(imageProcessor);
|
||||||
|
|
||||||
ApplicationUtils.setInstance(application);
|
ApplicationUtils.setInstance(application);
|
||||||
ss.info(this, "Appliation is configured.");
|
ss.info(this, "Appliation is configured.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -32,6 +32,8 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
|
||||||
import edu.cornell.mannlib.vitro.webapp.i18n.I18n;
|
import edu.cornell.mannlib.vitro.webapp.i18n.I18n;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions;
|
||||||
import edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil;
|
import edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -583,54 +585,6 @@ public class ImageUploadController extends FreemarkerHttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds the coordinates that we use to crop the main image.
|
|
||||||
*/
|
|
||||||
public static class CropRectangle {
|
|
||||||
public final int x;
|
|
||||||
public final int y;
|
|
||||||
public final int height;
|
|
||||||
public final int width;
|
|
||||||
|
|
||||||
public CropRectangle(int x, int y, int height, int width) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.height = height;
|
|
||||||
this.width = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Produce a new crop rectangle that compensates for scaling. */
|
|
||||||
public CropRectangle unscale(float scale) {
|
|
||||||
int newX = (int) (x / scale);
|
|
||||||
int newY = (int) (y / scale);
|
|
||||||
int newHeight = (int) (height / scale);
|
|
||||||
int newWidth = (int) (width / scale);
|
|
||||||
return new CropRectangle(newX, newY, newHeight, newWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "CropRectangle[x=" + x + ", y=" + y + ", w=" + width
|
|
||||||
+ ", h=" + height + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Dimensions {
|
|
||||||
final int width;
|
|
||||||
final int height;
|
|
||||||
|
|
||||||
Dimensions(int width, int height) {
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Dimensions[width=" + width + ", height=" + height + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getDefaultNamespace() {
|
private String getDefaultNamespace() {
|
||||||
return ConfigurationProperties.getBean(getServletContext())
|
return ConfigurationProperties.getBean(getServletContext())
|
||||||
.getProperty("Vitro.defaultNamespace");
|
.getProperty("Vitro.defaultNamespace");
|
||||||
|
|
|
@ -14,9 +14,6 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.media.jai.JAI;
|
|
||||||
import javax.media.jai.RenderedOp;
|
|
||||||
import javax.media.jai.util.ImagingListener;
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
import org.apache.commons.fileupload.FileItem;
|
import org.apache.commons.fileupload.FileItem;
|
||||||
|
@ -24,12 +21,9 @@ import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
|
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.CropRectangle;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.Dimensions;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.UserMistakeException;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.UserMistakeException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.TempFileHolder;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.TempFileHolder;
|
||||||
|
@ -37,6 +31,9 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.ImageProcessorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the mechanics of validating, storing, and deleting file images.
|
* Handle the mechanics of validating, storing, and deleting file images.
|
||||||
|
@ -95,15 +92,6 @@ public class ImageUploadHelper {
|
||||||
return Collections.unmodifiableMap(map);
|
return Collections.unmodifiableMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Prevent Java Advanced Imaging from complaining about the lack of
|
|
||||||
* accelerator classes.
|
|
||||||
*/
|
|
||||||
static {
|
|
||||||
JAI.getDefaultInstance().setImagingListener(
|
|
||||||
new NonNoisyImagingListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
private final FileStorage fileStorage;
|
private final FileStorage fileStorage;
|
||||||
private final UploadedFileHelper uploadedFileHelper;
|
private final UploadedFileHelper uploadedFileHelper;
|
||||||
|
|
||||||
|
@ -206,12 +194,8 @@ public class ImageUploadHelper {
|
||||||
String filename = fileInfo.getFilename();
|
String filename = fileInfo.getFilename();
|
||||||
|
|
||||||
source = fileStorage.getInputStream(uri, filename);
|
source = fileStorage.getInputStream(uri, filename);
|
||||||
MemoryCacheSeekableStream stream = new MemoryCacheSeekableStream(
|
Dimensions size = ApplicationUtils.instance().getImageProcessor()
|
||||||
source);
|
.getDimensions(source);
|
||||||
RenderedOp image = JAI.create("stream", stream);
|
|
||||||
|
|
||||||
Dimensions size = new Dimensions(image.getWidth(),
|
|
||||||
image.getHeight());
|
|
||||||
log.debug("new image size is " + size);
|
log.debug("new image size is " + size);
|
||||||
|
|
||||||
if ((size.height < THUMBNAIL_HEIGHT)
|
if ((size.height < THUMBNAIL_HEIGHT)
|
||||||
|
@ -272,8 +256,11 @@ public class ImageUploadHelper {
|
||||||
mainStream = fileStorage.getInputStream(mainBytestreamUri,
|
mainStream = fileStorage.getInputStream(mainBytestreamUri,
|
||||||
mainFilename);
|
mainFilename);
|
||||||
|
|
||||||
thumbStream = new ImageUploadThumbnailer(THUMBNAIL_HEIGHT,
|
thumbStream = ApplicationUtils
|
||||||
THUMBNAIL_WIDTH).cropAndScale(mainStream, crop);
|
.instance()
|
||||||
|
.getImageProcessor()
|
||||||
|
.cropAndScale(mainStream, crop,
|
||||||
|
new Dimensions(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT));
|
||||||
|
|
||||||
String mimeType = RECOGNIZED_FILE_TYPES.get(".jpg");
|
String mimeType = RECOGNIZED_FILE_TYPES.get(".jpg");
|
||||||
String filename = createThumbnailFilename(mainFilename);
|
String filename = createThumbnailFilename(mainFilename);
|
||||||
|
@ -288,6 +275,8 @@ public class ImageUploadHelper {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Can't create the thumbnail file",
|
throw new IllegalStateException("Can't create the thumbnail file",
|
||||||
e);
|
e);
|
||||||
|
} catch (ImageProcessorException e) {
|
||||||
|
throw new IllegalStateException("Failed to scale the image", e);
|
||||||
} finally {
|
} finally {
|
||||||
if (mainStream != null) {
|
if (mainStream != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -375,35 +364,4 @@ public class ImageUploadHelper {
|
||||||
return prefix + filename.substring(0, periodHere) + extension;
|
return prefix + filename.substring(0, periodHere) + extension;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* This {@link ImagingListener} means that Java Advanced Imaging won't dump
|
|
||||||
* an exception log to {@link System#out}. It writes to the log, instead.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Further, since the lack of native accelerator classes isn't an error, it
|
|
||||||
* is written as a simple log message.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
static class NonNoisyImagingListener implements ImagingListener {
|
|
||||||
@Override
|
|
||||||
public boolean errorOccurred(String message, Throwable thrown,
|
|
||||||
Object where, boolean isRetryable) throws RuntimeException {
|
|
||||||
if (thrown instanceof RuntimeException) {
|
|
||||||
throw (RuntimeException) thrown;
|
|
||||||
}
|
|
||||||
if ((thrown instanceof NoClassDefFoundError)
|
|
||||||
&& (thrown.getMessage()
|
|
||||||
.contains("com/sun/medialib/mlib/Image"))) {
|
|
||||||
log.info("Java Advanced Imaging: Could not find mediaLib "
|
|
||||||
+ "accelerator wrapper classes. "
|
|
||||||
+ "Continuing in pure Java mode.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
log.error(thrown, thrown);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
|
package edu.cornell.mannlib.vitro.webapp.imageprocessor.jai;
|
||||||
|
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.image.AffineTransformOp;
|
import java.awt.image.AffineTransformOp;
|
||||||
|
@ -12,16 +12,20 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.media.jai.JAI;
|
||||||
import javax.media.jai.RenderedOp;
|
import javax.media.jai.RenderedOp;
|
||||||
import javax.media.jai.operator.BandSelectDescriptor;
|
import javax.media.jai.operator.BandSelectDescriptor;
|
||||||
import javax.media.jai.operator.StreamDescriptor;
|
import javax.media.jai.operator.StreamDescriptor;
|
||||||
|
import javax.media.jai.util.ImagingListener;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
|
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.CropRectangle;
|
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crop the main image as specified, and scale it to the correct size for a
|
* Crop the main image as specified, and scale it to the correct size for a
|
||||||
|
@ -41,30 +45,44 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadControl
|
||||||
*
|
*
|
||||||
* Use the javax.imagio pacakge to write the thumbnail image as a JPEG file.
|
* Use the javax.imagio pacakge to write the thumbnail image as a JPEG file.
|
||||||
*/
|
*/
|
||||||
public class ImageUploadThumbnailer {
|
public class JaiImageProcessor implements ImageProcessor {
|
||||||
|
private static final Log log = LogFactory.getLog(JaiImageProcessor.class);
|
||||||
|
|
||||||
/** If an image has 3 color bands and 1 alpha band, we want these. */
|
/** If an image has 3 color bands and 1 alpha band, we want these. */
|
||||||
private static final int[] COLOR_BAND_INDEXES = new int[] { 0, 1, 2 };
|
private static final int[] COLOR_BAND_INDEXES = new int[] { 0, 1, 2 };
|
||||||
|
|
||||||
private static final Log log = LogFactory
|
/**
|
||||||
.getLog(ImageUploadThumbnailer.class);
|
* Prevent Java Advanced Imaging from complaining about the lack of
|
||||||
|
* accelerator classes.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void startup(Application application, ComponentStartupStatus ss) {
|
||||||
|
JAI.getDefaultInstance().setImagingListener(
|
||||||
|
new NonNoisyImagingListener());
|
||||||
|
}
|
||||||
|
|
||||||
/** We won't let you crop to smaller than this many pixels wide or high. */
|
@Override
|
||||||
private static final int MINIMUM_CROP_SIZE = 5;
|
public void shutdown(Application application) {
|
||||||
|
// Nothing to tear down.
|
||||||
|
}
|
||||||
|
|
||||||
private final int thumbnailHeight;
|
@Override
|
||||||
private final int thumbnailWidth;
|
public Dimensions getDimensions(InputStream imageStream)
|
||||||
|
throws ImageProcessorException, IOException {
|
||||||
public ImageUploadThumbnailer(int thumbnailHeight, int thumbnailWidth) {
|
MemoryCacheSeekableStream stream = new MemoryCacheSeekableStream(
|
||||||
this.thumbnailHeight = thumbnailHeight;
|
imageStream);
|
||||||
this.thumbnailWidth = thumbnailWidth;
|
RenderedOp image = JAI.create("stream", stream);
|
||||||
|
return new Dimensions(image.getWidth(), image.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crop the main image according to this rectangle, and scale it to the
|
* Crop the main image according to this rectangle, and scale it to the
|
||||||
* correct size for a thumbnail.
|
* correct size for a thumbnail.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public InputStream cropAndScale(InputStream mainImageStream,
|
public InputStream cropAndScale(InputStream mainImageStream,
|
||||||
CropRectangle crop) {
|
CropRectangle crop, Dimensions limits)
|
||||||
|
throws ImageProcessorException, IOException {
|
||||||
try {
|
try {
|
||||||
RenderedOp mainImage = loadImage(mainImageStream);
|
RenderedOp mainImage = loadImage(mainImageStream);
|
||||||
RenderedOp opaqueImage = makeImageOpaque(mainImage);
|
RenderedOp opaqueImage = makeImageOpaque(mainImage);
|
||||||
|
@ -77,7 +95,7 @@ public class ImageUploadThumbnailer {
|
||||||
bufferedImage, crop);
|
bufferedImage, crop);
|
||||||
log.debug("bounded crop: " + boundedCrop);
|
log.debug("bounded crop: " + boundedCrop);
|
||||||
|
|
||||||
float scaleFactor = figureScaleFactor(boundedCrop);
|
float scaleFactor = figureScaleFactor(boundedCrop, limits);
|
||||||
log.debug("scale factor: " + scaleFactor);
|
log.debug("scale factor: " + scaleFactor);
|
||||||
|
|
||||||
BufferedImage scaledImage = scaleImage(bufferedImage, scaleFactor);
|
BufferedImage scaledImage = scaleImage(bufferedImage, scaleFactor);
|
||||||
|
@ -100,10 +118,6 @@ public class ImageUploadThumbnailer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String imageSize(BufferedImage image) {
|
|
||||||
return image.getWidth() + " by " + image.getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
private RenderedOp loadImage(InputStream imageStream) {
|
private RenderedOp loadImage(InputStream imageStream) {
|
||||||
return StreamDescriptor.create(new MemoryCacheSeekableStream(
|
return StreamDescriptor.create(new MemoryCacheSeekableStream(
|
||||||
imageStream), null, null);
|
imageStream), null, null);
|
||||||
|
@ -127,6 +141,10 @@ public class ImageUploadThumbnailer {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String imageSize(BufferedImage image) {
|
||||||
|
return image.getWidth() + " by " + image.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
private CropRectangle limitCropRectangleToImageBounds(BufferedImage image,
|
private CropRectangle limitCropRectangleToImageBounds(BufferedImage image,
|
||||||
CropRectangle crop) {
|
CropRectangle crop) {
|
||||||
|
|
||||||
|
@ -150,18 +168,14 @@ public class ImageUploadThumbnailer {
|
||||||
return new CropRectangle(x, y, h, w);
|
return new CropRectangle(x, y, h, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float figureScaleFactor(CropRectangle boundedCrop) {
|
private float figureScaleFactor(CropRectangle boundedCrop, Dimensions limits) {
|
||||||
float horizontalScale = ((float) thumbnailWidth)
|
float horizontalScale = ((float) limits.width)
|
||||||
/ ((float) boundedCrop.width);
|
/ ((float) boundedCrop.width);
|
||||||
float verticalScale = ((float) thumbnailHeight)
|
float verticalScale = ((float) limits.height)
|
||||||
/ ((float) boundedCrop.height);
|
/ ((float) boundedCrop.height);
|
||||||
return Math.min(horizontalScale, verticalScale);
|
return Math.min(horizontalScale, verticalScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedImage cropImage(BufferedImage image, CropRectangle crop) {
|
|
||||||
return image.getSubimage(crop.x, crop.y, crop.width, crop.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BufferedImage scaleImage(BufferedImage image, float scaleFactor) {
|
private BufferedImage scaleImage(BufferedImage image, float scaleFactor) {
|
||||||
AffineTransform transform = AffineTransform.getScaleInstance(
|
AffineTransform transform = AffineTransform.getScaleInstance(
|
||||||
scaleFactor, scaleFactor);
|
scaleFactor, scaleFactor);
|
||||||
|
@ -178,9 +192,42 @@ public class ImageUploadThumbnailer {
|
||||||
return new CropRectangle(newX, newY, newHeight, newWidth);
|
return new CropRectangle(newX, newY, newHeight, newWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BufferedImage cropImage(BufferedImage image, CropRectangle crop) {
|
||||||
|
return image.getSubimage(crop.x, crop.y, crop.width, crop.height);
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] encodeAsJpeg(BufferedImage image) throws IOException {
|
private byte[] encodeAsJpeg(BufferedImage image) throws IOException {
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
ImageIO.write(image, "JPG", bytes);
|
ImageIO.write(image, "JPG", bytes);
|
||||||
return bytes.toByteArray();
|
return bytes.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This ImagingListener means that Java Advanced Imaging won't dump an
|
||||||
|
* exception log to System.out. It writes to the log, instead.
|
||||||
|
*
|
||||||
|
* Further, since the lack of native accelerator classes isn't an error, it
|
||||||
|
* is written as a simple log message.
|
||||||
|
*/
|
||||||
|
static class NonNoisyImagingListener implements ImagingListener {
|
||||||
|
@Override
|
||||||
|
public boolean errorOccurred(String message, Throwable thrown,
|
||||||
|
Object where, boolean isRetryable) throws RuntimeException {
|
||||||
|
if (thrown instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) thrown;
|
||||||
|
}
|
||||||
|
if ((thrown instanceof NoClassDefFoundError)
|
||||||
|
&& (thrown.getMessage()
|
||||||
|
.contains("com/sun/medialib/mlib/Image"))) {
|
||||||
|
log.info("Java Advanced Imaging: Could not find mediaLib "
|
||||||
|
+ "accelerator wrapper classes. "
|
||||||
|
+ "Continuing in pure Java mode.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log.error(thrown, thrown);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.modules;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +15,8 @@ public interface Application {
|
||||||
|
|
||||||
SearchEngine getSearchEngine();
|
SearchEngine getSearchEngine();
|
||||||
|
|
||||||
|
ImageProcessor getImageProcessor();
|
||||||
|
|
||||||
public interface Component {
|
public interface Component {
|
||||||
enum LifecycleState {
|
enum LifecycleState {
|
||||||
NEW, ACTIVE, STOPPED
|
NEW, ACTIVE, STOPPED
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.modules.imageProcessor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle image processing for VIVO; uploads and thumbnailing.
|
||||||
|
*/
|
||||||
|
public interface ImageProcessor extends Application.Module {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We won't let you crop to smaller than this many pixels wide or high.
|
||||||
|
*/
|
||||||
|
public static final int MINIMUM_CROP_SIZE = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How big is the image contained in this stream?
|
||||||
|
*
|
||||||
|
* @param image
|
||||||
|
* The image stream. This method will not close it.
|
||||||
|
* @return The dimensions of the image. Never returns null.
|
||||||
|
* @throws ImageProcessorException
|
||||||
|
* if the stream does not contain a valid image.
|
||||||
|
* @throws IOException
|
||||||
|
* if the stream cannot be read.
|
||||||
|
*/
|
||||||
|
Dimensions getDimensions(InputStream image) throws ImageProcessorException,
|
||||||
|
IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new image from a portion of the supplied image, and reduce to
|
||||||
|
* fit a limiting size.
|
||||||
|
*
|
||||||
|
* If the crop rectangle extends beyond the image boundaries, it is limited
|
||||||
|
* to fit, or relocated if limiting would make it smaller than the
|
||||||
|
* MINIMUM_CROP_SIZE.
|
||||||
|
*
|
||||||
|
* @param image
|
||||||
|
* The image stream. This method will not close it.
|
||||||
|
* @param crop
|
||||||
|
* x and y determine the upper left corner of the crop area.
|
||||||
|
* height and width determine the size of the crop area.
|
||||||
|
* @param limits
|
||||||
|
* The resulting image will be reduced as necessary to fit within
|
||||||
|
* these dimensions.
|
||||||
|
* @return The new image. Client code should close this stream after
|
||||||
|
* reading. Never returns null.
|
||||||
|
* @throws ImageProcessorException
|
||||||
|
* If the image is smaller than the minimum crop size, or if
|
||||||
|
* there is another problem cropping the image.
|
||||||
|
* @throws IOException
|
||||||
|
* if the image stream cannot be read.
|
||||||
|
*/
|
||||||
|
InputStream cropAndScale(InputStream image, CropRectangle crop,
|
||||||
|
Dimensions limits) throws ImageProcessorException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A problem with the image.
|
||||||
|
*/
|
||||||
|
public static class ImageProcessorException extends Exception {
|
||||||
|
public ImageProcessorException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageProcessorException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of a rectangular image, in pixels.
|
||||||
|
*/
|
||||||
|
public static class Dimensions {
|
||||||
|
public final int width;
|
||||||
|
public final int height;
|
||||||
|
|
||||||
|
public Dimensions(int width, int height) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ImageProcessor.Dimensions[width=" + width + ", height="
|
||||||
|
+ height + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the coordinates that we use to crop an image.
|
||||||
|
*/
|
||||||
|
public static class CropRectangle {
|
||||||
|
public final int x;
|
||||||
|
public final int y;
|
||||||
|
public final int height;
|
||||||
|
public final int width;
|
||||||
|
|
||||||
|
public CropRectangle(int x, int y, int height, int width) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.height = height;
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "CropRectangle[x=" + x + ", y=" + y + ", w=" + width
|
||||||
|
+ ", h=" + height + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
|
package edu.cornell.mannlib.vitro.webapp.imageprocessor.jai;
|
||||||
|
|
||||||
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_HEIGHT;
|
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_HEIGHT;
|
||||||
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_WIDTH;
|
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_WIDTH;
|
||||||
|
@ -17,7 +17,6 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import javax.media.jai.JAI;
|
|
||||||
import javax.media.jai.RenderedOp;
|
import javax.media.jai.RenderedOp;
|
||||||
import javax.media.jai.operator.StreamDescriptor;
|
import javax.media.jai.operator.StreamDescriptor;
|
||||||
|
|
||||||
|
@ -26,7 +25,9 @@ import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
|
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadHelper.NonNoisyImagingListener;
|
import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessor;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is not a unit test, so it is not named BlahBlahTest.
|
* This is not a unit test, so it is not named BlahBlahTest.
|
||||||
|
@ -38,14 +39,13 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadHelper.
|
||||||
* This is especially true because the images on the screen look color-correct,
|
* This is especially true because the images on the screen look color-correct,
|
||||||
* but when viewed in the browser, they might not be.
|
* but when viewed in the browser, they might not be.
|
||||||
*/
|
*/
|
||||||
public class ImageUploaderThumbnailerTester extends Frame {
|
public class JaiImageProcessorTester extends Frame {
|
||||||
static {
|
|
||||||
JAI.getDefaultInstance().setImagingListener(
|
|
||||||
new NonNoisyImagingListener());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Big enough to hold the JPEG file, certainly. */
|
/** Big enough to hold the JPEG file, certainly. */
|
||||||
private final static int BUFFER_SIZE = 200 * 200 * 4;
|
private final static int BUFFER_SIZE = 200 * 200 * 4;
|
||||||
|
|
||||||
|
private final static Dimensions THUMBNAIL_SIZE = new Dimensions(
|
||||||
|
THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
|
||||||
|
|
||||||
private final static ImageCropData[] THUMBNAIL_DATA = new ImageCropData[] {
|
private final static ImageCropData[] THUMBNAIL_DATA = new ImageCropData[] {
|
||||||
new ImageCropData("/Users/jeb228/Pictures/JimBlake_20010915.jpg",
|
new ImageCropData("/Users/jeb228/Pictures/JimBlake_20010915.jpg",
|
||||||
|
@ -56,11 +56,10 @@ public class ImageUploaderThumbnailerTester extends Frame {
|
||||||
new ImageCropData("/Users/jeb228/Pictures/DSC04203w-trans.gif",
|
new ImageCropData("/Users/jeb228/Pictures/DSC04203w-trans.gif",
|
||||||
400, 1200, 800) };
|
400, 1200, 800) };
|
||||||
|
|
||||||
private final ImageUploadThumbnailer thumbnailer = new ImageUploadThumbnailer(
|
private final JaiImageProcessor thumbnailer = new JaiImageProcessor();
|
||||||
THUMBNAIL_HEIGHT, THUMBNAIL_WIDTH);
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private ImageUploaderThumbnailerTester() {
|
private JaiImageProcessorTester() {
|
||||||
setTitle("Alpha Killer Test");
|
setTitle("Alpha Killer Test");
|
||||||
addWindowListener(new CloseWindowListener());
|
addWindowListener(new CloseWindowListener());
|
||||||
setLayout(createLayout());
|
setLayout(createLayout());
|
||||||
|
@ -68,7 +67,7 @@ public class ImageUploaderThumbnailerTester extends Frame {
|
||||||
try {
|
try {
|
||||||
InputStream mainStream = new FileInputStream(icd.filename);
|
InputStream mainStream = new FileInputStream(icd.filename);
|
||||||
File thumbFile = writeToTempFile(thumbnailer.cropAndScale(
|
File thumbFile = writeToTempFile(thumbnailer.cropAndScale(
|
||||||
mainStream, icd.crop));
|
mainStream, icd.crop, THUMBNAIL_SIZE));
|
||||||
System.out.println(thumbFile.getAbsolutePath());
|
System.out.println(thumbFile.getAbsolutePath());
|
||||||
|
|
||||||
MemoryCacheSeekableStream thumbFileStream = new MemoryCacheSeekableStream(
|
MemoryCacheSeekableStream thumbFileStream = new MemoryCacheSeekableStream(
|
||||||
|
@ -108,18 +107,19 @@ public class ImageUploaderThumbnailerTester extends Frame {
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Logger.getLogger(ImageUploadThumbnailer.class).setLevel(Level.DEBUG);
|
Logger.getLogger(JaiImageProcessor.class).setLevel(Level.DEBUG);
|
||||||
new ImageUploaderThumbnailerTester();
|
new JaiImageProcessorTester();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ImageCropData {
|
private static class ImageCropData {
|
||||||
final String filename;
|
final String filename;
|
||||||
final ImageUploadController.CropRectangle crop;
|
final CropRectangle crop;
|
||||||
|
|
||||||
ImageCropData(String filename, int x, int y, int size) {
|
ImageCropData(String filename, int x, int y, int size) {
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.crop = new ImageUploadController.CropRectangle(x, y, size,
|
this.crop = new CropRectangle(x, y, size,
|
||||||
size);
|
size);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
|
package edu.cornell.mannlib.vitro.webapp.imageprocessor.jai;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
@ -35,9 +35,10 @@ import org.apache.log4j.PatternLayout;
|
||||||
|
|
||||||
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
|
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.CropRectangle;
|
import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessorTester2.CropDataSet.CropData;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadHelper.NonNoisyImagingListener;
|
import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessor.NonNoisyImagingListener;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploaderThumbnailerTester_2.CropDataSet.CropData;
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is not a unit test, so it is not named BlahBlahTest.
|
* This is not a unit test, so it is not named BlahBlahTest.
|
||||||
|
@ -49,14 +50,16 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploaderThumb
|
||||||
* one or more black edges on the thumbnails.
|
* one or more black edges on the thumbnails.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class ImageUploaderThumbnailerTester_2 extends Frame {
|
public class JaiImageProcessorTester2 extends Frame {
|
||||||
private static final Log log = LogFactory
|
private static final Log log = LogFactory
|
||||||
.getLog(ImageUploaderThumbnailerTester_2.class);
|
.getLog(JaiImageProcessorTester2.class);
|
||||||
|
|
||||||
private static final int ROWS = 6;
|
private static final int ROWS = 6;
|
||||||
private static final int COLUMNS = 9;
|
private static final int COLUMNS = 9;
|
||||||
|
|
||||||
private static final int EDGE_THRESHOLD = 6000;
|
private static final int EDGE_THRESHOLD = 6000;
|
||||||
|
|
||||||
|
private static final Dimensions THUMBNAIL_SIZE = new Dimensions(200, 200);
|
||||||
|
|
||||||
/** Keep things quiet. */
|
/** Keep things quiet. */
|
||||||
static {
|
static {
|
||||||
|
@ -65,12 +68,12 @@ public class ImageUploaderThumbnailerTester_2 extends Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String imagePath;
|
private final String imagePath;
|
||||||
private final ImageUploadThumbnailer thumbnailer;
|
private final JaiImageProcessor thumbnailer;
|
||||||
|
|
||||||
public ImageUploaderThumbnailerTester_2(String imagePath,
|
public JaiImageProcessorTester2(String imagePath,
|
||||||
CropDataSet cropDataSet) {
|
CropDataSet cropDataSet) {
|
||||||
this.imagePath = imagePath;
|
this.imagePath = imagePath;
|
||||||
this.thumbnailer = new ImageUploadThumbnailer(200, 200);
|
this.thumbnailer = new JaiImageProcessor();
|
||||||
|
|
||||||
setTitle("Cropping edging test");
|
setTitle("Cropping edging test");
|
||||||
addWindowListener(new CloseWindowListener());
|
addWindowListener(new CloseWindowListener());
|
||||||
|
@ -119,7 +122,7 @@ public class ImageUploaderThumbnailerTester_2 extends Frame {
|
||||||
CropRectangle rectangle = new CropRectangle(cropData.left,
|
CropRectangle rectangle = new CropRectangle(cropData.left,
|
||||||
cropData.top, cropData.size, cropData.size);
|
cropData.top, cropData.size, cropData.size);
|
||||||
InputStream thumbnailStream = thumbnailer.cropAndScale(mainStream,
|
InputStream thumbnailStream = thumbnailer.cropAndScale(mainStream,
|
||||||
rectangle);
|
rectangle, THUMBNAIL_SIZE);
|
||||||
|
|
||||||
return StreamDescriptor.create(new MemoryCacheSeekableStream(
|
return StreamDescriptor.create(new MemoryCacheSeekableStream(
|
||||||
thumbnailStream), null, null);
|
thumbnailStream), null, null);
|
||||||
|
@ -200,8 +203,8 @@ public class ImageUploaderThumbnailerTester_2 extends Frame {
|
||||||
.nextElement();
|
.nextElement();
|
||||||
appender.setLayout(new PatternLayout("%-5p [%c{1}] %m%n"));
|
appender.setLayout(new PatternLayout("%-5p [%c{1}] %m%n"));
|
||||||
|
|
||||||
Logger.getLogger(ImageUploadThumbnailer.class).setLevel(Level.DEBUG);
|
Logger.getLogger(JaiImageProcessor.class).setLevel(Level.DEBUG);
|
||||||
Logger.getLogger(ImageUploaderThumbnailerTester_2.class).setLevel(
|
Logger.getLogger(JaiImageProcessorTester2.class).setLevel(
|
||||||
Level.INFO);
|
Level.INFO);
|
||||||
|
|
||||||
CropDataSet cropDataSet = new CropDataSet();
|
CropDataSet cropDataSet = new CropDataSet();
|
||||||
|
@ -210,7 +213,7 @@ public class ImageUploaderThumbnailerTester_2 extends Frame {
|
||||||
cropDataSet.add(0, 0, 201 + i);
|
cropDataSet.add(0, 0, 201 + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
new ImageUploaderThumbnailerTester_2(
|
new JaiImageProcessorTester2(
|
||||||
"C:/Users/jeb228/Pictures/wheel.png", cropDataSet);
|
"C:/Users/jeb228/Pictures/wheel.png", cropDataSet);
|
||||||
|
|
||||||
// new ImageUploaderThumbnailerTester_2(
|
// new ImageUploaderThumbnailerTester_2(
|
|
@ -8,6 +8,7 @@ import javax.servlet.ServletContext;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
|
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
import edu.cornell.mannlib.vitro.webapp.modules.Application;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
|
||||||
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,4 +59,11 @@ public class ApplicationStub implements Application {
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Un-implemented methods
|
// Un-implemented methods
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageProcessor getImageProcessor() {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"ApplicationStub.getImageProcessor() not implemented.");
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue