From 5a2af2f97d979d07b21cc1b61e01e16b533cccc3 Mon Sep 17 00:00:00 2001 From: jeb228 Date: Tue, 12 Oct 2010 20:05:55 +0000 Subject: [PATCH] NIHVIVO-1208 Cleaned things up a bit - ready to give up, for now at least. Still works for JPEG, but not for GIF. --- .../freemarker/ImageUploadHelper.java | 2 +- .../freemarker/ImageUploadThumbnailer.java | 33 +++-- .../ImageUploaderThumbnailerTester.java | 135 ++++++++++++++++++ 3 files changed, 154 insertions(+), 16 deletions(-) create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploaderThumbnailerTester.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadHelper.java index b0bbe9d2f..c4f2114a2 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadHelper.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadHelper.java @@ -393,7 +393,7 @@ public class ImageUploadHelper { * is written as a simple log message. *

*/ - private static class NonNoisyImagingListener implements ImagingListener { + static class NonNoisyImagingListener implements ImagingListener { @Override public boolean errorOccurred(String message, Throwable thrown, Object where, boolean isRetryable) throws RuntimeException { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadThumbnailer.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadThumbnailer.java index 4b62f43f4..df47bc0d8 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadThumbnailer.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploadThumbnailer.java @@ -7,7 +7,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; import javax.media.jai.InterpolationBilinear; import javax.media.jai.RenderedOp; @@ -28,8 +27,15 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadControl /** * Crop the main image as specified, and scale it to the correct size for a * thumbnail. + * + * The JAI library has a problem when writing a JPEG from a source image with an + * alpha channel (transparency). The colors come out inverted. We throw in a + * step that will remove transparency from a PNG, but it won't touch a GIF. */ public class ImageUploadThumbnailer { + /** 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 Log log = LogFactory .getLog(ImageUploadThumbnailer.class); @@ -69,11 +75,20 @@ public class ImageUploadThumbnailer { private RenderedOp makeImageOpaque(RenderedOp image) { ColorModel colorModel = image.getColorModel(); + if (!colorModel.hasAlpha()) { + // The image is already opaque. return image; } - return BandSelectDescriptor.create(image, figureBandIndices(image), - null); + + if (image.getNumBands() == 4) { + // The image has a separate alpha channel. Drop the alpha channel. + return BandSelectDescriptor.create(image, COLOR_BAND_INDEXES, null); + } + + // Don't know how to handle it. Probably a GIF with a transparent + // background. Give up. + return image; } private RenderedOp cropImage(RenderedOp image, CropRectangle crop) { @@ -104,18 +119,6 @@ public class ImageUploadThumbnailer { return bytes.toByteArray(); } - /** Build an array holding the indexes of the color bands in this image. */ - private int[] figureBandIndices(RenderedOp image) { - int howMany = Math.min(image.getColorModel().getNumColorComponents(), - image.getNumBands()); - int[] bandIndices = new int[howMany]; - for (int i = 0; i < bandIndices.length; i++) { - bandIndices[i] = i; - } - log.debug("Selecting these bands: " + Arrays.toString(bandIndices)); - return bandIndices; - } - private CropRectangle limitCropRectangleToImageBounds(RenderedOp image, CropRectangle crop) { log.debug("Generating a thumbnail, initial crop info: " + crop); diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploaderThumbnailerTester.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploaderThumbnailerTester.java new file mode 100644 index 000000000..a671bbbf9 --- /dev/null +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ImageUploaderThumbnailerTester.java @@ -0,0 +1,135 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.controller.freemarker; + +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 java.awt.Frame; +import java.awt.GridLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.media.jai.JAI; +import javax.media.jai.RenderedOp; +import javax.media.jai.operator.StreamDescriptor; +import javax.media.jai.widget.ImageCanvas; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +import com.sun.media.jai.codec.MemoryCacheSeekableStream; + +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadHelper.NonNoisyImagingListener; + +/** + * This is not a unit test, so it is not named BlahBlahTest. + * + * Instead, it's a test harness that creates thumbnails and writes them to + * files, while also displaying them in a window on the screen. It takes human + * intervention to evaluate. + * + * This is especially true because the images on the screen look color-correct, + * but when viewed in the browser, they might not be. + */ +public class ImageUploaderThumbnailerTester extends Frame { + static { + JAI.getDefaultInstance().setImagingListener( + new NonNoisyImagingListener()); + } + + /** Big enough to hold the JPEG file, certainly. */ + private final static int BUFFER_SIZE = 200 * 200 * 4; + + private final static ImageCropData[] THUMBNAIL_DATA = new ImageCropData[] { + new ImageCropData("/Users/jeb228/Pictures/JimBlake_20010915.jpg", + 50, 50, 115), + new ImageCropData("/Users/jeb228/Pictures/brazil_collab.png", 600, + 250, 400), + new ImageCropData("/Users/jeb228/Pictures/wheel.png", 0, 0, 195), + new ImageCropData("/Users/jeb228/Pictures/DSC04203w-trans.gif", + 400, 1200, 800) }; + + private final ImageUploadThumbnailer thumbnailer = new ImageUploadThumbnailer( + THUMBNAIL_HEIGHT, THUMBNAIL_WIDTH); + + private ImageUploaderThumbnailerTester() { + setTitle("Alpha Killer Test"); + addWindowListener(new CloseWindowListener()); + setLayout(createLayout()); + for (ImageCropData icd : THUMBNAIL_DATA) { + try { + InputStream mainStream = new FileInputStream(icd.filename); + File thumbFile = writeToTempFile(thumbnailer.cropAndScale( + mainStream, icd.crop)); + System.out.println(thumbFile.getAbsolutePath()); + + MemoryCacheSeekableStream thumbFileStream = new MemoryCacheSeekableStream( + new FileInputStream(thumbFile)); + RenderedOp thumbImage = StreamDescriptor.create( + thumbFileStream, null, null); + add(new ImageCanvas(thumbImage)); + } catch (Exception e) { + e.printStackTrace(); + } + } + pack(); + setVisible(true); + } + + /** + * @param thumbStream + * @return + * @throws IOException + * @throws FileNotFoundException + */ + private File writeToTempFile(InputStream thumbStream) throws IOException, + FileNotFoundException { + File thumbFile = File.createTempFile("ImageUploaderThumbnailerTester", + ""); + OutputStream imageOutputStream = new FileOutputStream(thumbFile); + byte[] buffer = new byte[BUFFER_SIZE]; + int howMany = thumbStream.read(buffer); + imageOutputStream.write(buffer, 0, howMany); + imageOutputStream.close(); + return thumbFile; + } + + private GridLayout createLayout() { + GridLayout layout = new GridLayout(1, THUMBNAIL_DATA.length); + layout.setHgap(10); + return layout; + } + + public static void main(String[] args) { + Logger.getLogger(ImageUploadThumbnailer.class).setLevel(Level.DEBUG); + new ImageUploaderThumbnailerTester(); + } + + private static class ImageCropData { + final String filename; + final ImageUploadController.CropRectangle crop; + + ImageCropData(String filename, int x, int y, int size) { + this.filename = filename; + this.crop = new ImageUploadController.CropRectangle(x, y, size, + size); + } + } + + private class CloseWindowListener extends WindowAdapter { + @Override + public void windowClosing(WindowEvent e) { + setVisible(false); + dispose(); + System.exit(0); + } + } +}