Various changes to the imaging code:

- added Image.trim(), that trims an image based on a specified pixel, just like in Photoshop.
- saveAs exists in two versions now, one that takes a filename, the other an OutputStream + Mime type (first step toward an  Java activation framework based approach)
This commit is contained in:
lehni 2005-08-12 11:28:55 +00:00
parent e78b126148
commit 29f4faa3a1
5 changed files with 188 additions and 71 deletions

View file

@ -21,6 +21,7 @@ import helma.main.Server;
import java.awt.*;
import java.awt.image.*;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
@ -208,4 +209,16 @@ public abstract class ImageGenerator {
*/
public abstract void write(ImageWrapper wrapper, String filename,
float quality, boolean alpha) throws IOException;
/**
* Saves the image. Image format is deduced from the dataSource.
*
* @param wrapper
* @param out
* @param quality
* @param alpha
* @throws IOException
*/
public abstract void write(ImageWrapper wrapper, OutputStream out, String type,
float quality, boolean alpha) throws IOException;
}

View file

@ -221,6 +221,7 @@ public class ImageInfo {
private int width;
private int height;
private int bitsPerPixel;
private int numColors;
private int colorType = COLOR_TYPE_UNKNOWN;
private boolean progressive;
private int format;
@ -257,6 +258,7 @@ public class ImageInfo {
numberOfImages = 1;
physicalHeightDpi = -1;
physicalWidthDpi = -1;
numColors = -1;
comments = null;
try {
int b1 = read() & 0xff;
@ -353,14 +355,14 @@ public class ImageInfo {
int flags = a[8] & 0xff;
bitsPerPixel = ((flags >> 4) & 0x07) + 1;
progressive = (flags & 0x02) != 0;
if (!determineNumberOfImages) {
return true;
}
// skip global color palette
if ((flags & 0x80) != 0) {
int tableSize = (1 << ((flags & 7) + 1)) * 3;
skip(tableSize);
numColors = (1 << ((flags & 7) + 1));
skip(numColors * 3);
}
if (!determineNumberOfImages) {
return true;
}
numberOfImages = 0;
int blockType;
do
@ -379,7 +381,11 @@ public class ImageInfo {
bitsPerPixel = localBitsPerPixel;
}
if ((flags & 0x80) != 0) {
skip((1 << localBitsPerPixel) * 3);
int localNumColors = 1 << localBitsPerPixel;
skip(localNumColors * 3);
if (localNumColors > numColors) {
numColors = localNumColors;
}
}
skip(1); // initial code length
int n;
@ -777,7 +783,14 @@ public class ImageInfo {
return bitsPerPixel;
}
/**
/**
* @return number of colors, for palette based images
*/
public int getNumColors() {
return numColors;
}
/**
* Returns the index'th comment retrieved from the image.
* @throws IllegalArgumentException if index is smaller than 0 or larger than or equal
* to the number of comments retrieved

View file

@ -785,10 +785,6 @@ public class Quantize {
int db = b1 - b2;
return da * da + dr * dr + dg * dg + db * db;
// return (SQUARES[r1 - r2 + MAX_RGB] +
// SQUARES[g1 - g2 + MAX_RGB] +
// SQUARES[b1 - b2 + MAX_RGB] +
// SQUARES[a1 - a2 + MAX_RGB]);
}
public String toString() {

View file

@ -71,6 +71,43 @@ public class ImageIOGenerator extends ImageGenerator {
return ImageIO.read(new ByteArrayInputStream(src));
}
protected void write(ImageWrapper wrapper, ImageWriter writer, float quality, boolean alpha) throws IOException {
BufferedImage bi = wrapper.getBufferedImage();
// Set some parameters
ImageWriteParam param = writer.getDefaultWriteParam();
if (param.canWriteCompressed() &&
quality >= 0.0 && quality <= 1.0) {
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
}
if (param.canWriteProgressive())
param.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
// if bi has type ARGB and alpha is false, we have to tell the writer to not use the alpha channel:
// this is especially needed for jpeg files where imageio seems to produce wrong jpeg files right now...
if (bi.getType() == BufferedImage.TYPE_INT_ARGB
&& !alpha) {
// create a new BufferedImage that uses a WritableRaster of bi, with all the bands except the alpha band:
WritableRaster raster = bi.getRaster();
WritableRaster newRaster = raster.createWritableChild(
0, 0, raster.getWidth(), raster.getHeight(),
0, 0, new int[] {0, 1, 2 }
);
// create a ColorModel that represents the one of the ARGB except the alpha channel:
DirectColorModel cm = (DirectColorModel) bi.getColorModel();
DirectColorModel newCM = new DirectColorModel(
cm.getPixelSize(), cm.getRedMask(),
cm.getGreenMask(), cm.getBlueMask());
// now create the new buffer that is used ot write the image:
BufferedImage rgbBuffer = new BufferedImage(newCM,
newRaster, false, null);
writer.write(null, new IIOImage(rgbBuffer, null,
null), param);
} else {
writer.write(null, new IIOImage(bi, null, null),
param);
}
}
/**
* Saves the image. Image format is deduced from filename.
*
@ -81,65 +118,62 @@ public class ImageIOGenerator extends ImageGenerator {
* @see helma.image.ImageGenerator#write(helma.image.ImageWrapper, java.lang.String, float, boolean)
*/
public void write(ImageWrapper wrapper, String filename, float quality, boolean alpha) throws IOException {
// determine suffix:
int pos = filename.lastIndexOf('.');
if (pos != -1) {
String extension = filename.substring(pos + 1,
filename.length()).toLowerCase();
String extension = filename.substring(pos + 1, filename.length()).toLowerCase();
// Find a writer for that file extensions
// Find a writer for that file suffix
ImageWriter writer = null;
Iterator iter = ImageIO.getImageWritersByFormatName(extension);
Iterator iter = ImageIO.getImageWritersBySuffix(extension);
if (iter.hasNext())
writer = (ImageWriter) iter.next();
writer = (ImageWriter)iter.next();
if (writer != null) {
ImageOutputStream ios = null;
try {
BufferedImage bi = wrapper.getBufferedImage();
// Prepare output file
File file = new File(filename);
if (file.exists())
file.delete();
ios = ImageIO.createImageOutputStream(file);
writer.setOutput(ios);
// Set some parameters
ImageWriteParam param = writer.getDefaultWriteParam();
if (param.canWriteCompressed() &&
quality >= 0.0 && quality <= 1.0) {
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(quality);
}
if (param.canWriteProgressive())
param.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
// if bi has type ARGB and alpha is false, we have to tell the writer to not use the alpha channel:
// this is especially needed for jpeg files where imageio seems to produce wrong jpeg files right now...
if (bi.getType() == BufferedImage.TYPE_INT_ARGB
&& !alpha) {
// create a new BufferedImage that uses a WritableRaster of bi, with all the bands except the alpha band:
WritableRaster raster = bi.getRaster();
WritableRaster newRaster = raster.createWritableChild(
0, 0, wrapper.getWidth(), wrapper.getHeight(),
0, 0, new int[] {0, 1, 2 }
);
// create a ColorModel that represents the one of the ARGB except the alpha channel:
DirectColorModel cm = (DirectColorModel) bi.getColorModel();
DirectColorModel newCM = new DirectColorModel(
cm.getPixelSize(), cm.getRedMask(),
cm.getGreenMask(), cm.getBlueMask());
// now create the new buffer that is used ot write the image:
BufferedImage rgbBuffer = new BufferedImage(newCM,
newRaster, false, null);
writer.write(null, new IIOImage(rgbBuffer, null,
null), param);
} else {
writer.write(null, new IIOImage(bi, null, null),
param);
}
} finally {
this.write(wrapper, writer, quality, alpha);
} finally {
if (ios != null)
ios.close();
writer.dispose();
}
}
}
}
}
/**
* Saves the image. Image format is deduced from type.
*
* @param out ...
* @param type ...
* @param quality ...
* @param alpha ...
* @throws IOException
* @see helma.image.ImageGenerator#write(helma.image.ImageWrapper, java.io.OutputStream, java.lang.String, float, boolean)
*/
public void write(ImageWrapper wrapper, OutputStream out, String mimeType, float quality, boolean alpha) throws IOException {
// Find a writer for that type
ImageWriter writer = null;
Iterator iter = ImageIO.getImageWritersByMIMEType(mimeType);
if (iter.hasNext())
writer = (ImageWriter)iter.next();
if (writer != null) {
ImageOutputStream ios = null;
try {
ios = ImageIO.createImageOutputStream(out);
writer.setOutput(ios);
this.write(wrapper, writer, quality, alpha);
} finally {
if (ios != null)
ios.close();
writer.dispose();
}
}
}
}

View file

@ -28,25 +28,24 @@ import com.sun.jimi.core.options.JPGOptions;
import com.sun.jimi.core.options.PNGOptions;
public class JimiGenerator extends ImageGenerator {
/**
* Saves the image. Image format is deduced from filename.
* Internal function for writing images.
*
* @param filename ...
* @param wrapper ...
* @param type either a file extension or a mimetype with stripped image/ or image/x-
* @param quality ...
* @param alpha ...
* @throws IOException
* @see helma.image.ImageGenerator#write(helma.image.ImageWrapper, java.lang.String, float, boolean)
*/
public void write(ImageWrapper wrapper, String filename, float quality, boolean alpha) throws IOException {
protected boolean write(ImageWrapper wrapper, String type, OutputStream out, float quality, boolean alpha) throws IOException {
try {
String lowerCaseName = filename.toLowerCase();
if (lowerCaseName.endsWith(".gif")) {
if ("gif".equals(type)) {
// sun's jimi package doesn't encode gifs, use helma's encoder instead
DataOutputStream out = new DataOutputStream(
new FileOutputStream(filename));
DataOutputStream dataOut = new DataOutputStream(out);
GIFEncoder encoder = new GIFEncoder();
encoder.encode(wrapper.getBufferedImage(), out);
out.close();
encoder.encode(wrapper.getBufferedImage(), dataOut);
} else {
// let's not rely on Jimi's file-extension detecting mechanisms,
// as these do not seem to specify file type depending options
@ -54,8 +53,7 @@ public class JimiGenerator extends ImageGenerator {
JimiImage source = Jimi.createRasterImage(wrapper.getSource());
JimiEncoder encoder = null;
if (lowerCaseName.endsWith(".jpg")
|| lowerCaseName.endsWith(".jpeg")) {
if ("jpg".equals(type) || "jpeg".equals(type)) {
// JPEG
encoder = new JPGEncoder();
// the quality value does mean something here and can be specified:
@ -64,7 +62,7 @@ public class JimiGenerator extends ImageGenerator {
options.setQuality(Math.round(quality * 100));
source.setOptions(options);
}
} else if (lowerCaseName.endsWith(".png")) {
} else if ("png".equals(type)) {
// PNG
encoder = new PNGEncoder();
// the alpha parameter does mean something here:
@ -74,17 +72,80 @@ public class JimiGenerator extends ImageGenerator {
options.setCompressionType(PNGOptions.COMPRESSION_MAX);
source.setOptions(options);
}
if (encoder != null) {
FileOutputStream out = new FileOutputStream(filename);
encoder.encodeImages(new JimiImageEnumeration(source), out);
out.close();
} else { // if nothing worked, fall back to the Jimi mechanisms and see wether something comes out
Jimi.putImage(wrapper.getImage(), filename);
}
// if no encoder was found, return false. let jimi handle this in the functions bellow
if (encoder == null) return false;
encoder.encodeImages(new JimiImageEnumeration(source), out);
}
return true;
} catch (JimiException e) {
throw new IOException(e.getMessage());
}
}
/**
* Saves the image. Image format is deduced from filename.
*
* @param wrapper ...
* @param filename ...
* @param quality ...
* @param alpha ...
* @throws IOException
* @see helma.image.ImageGenerator#write(helma.image.ImageWrapper, java.lang.String, float, boolean)
*/
public void write(ImageWrapper wrapper, String filename, float quality, boolean alpha) throws IOException {
// determine the type from the file extension
int pos = filename.lastIndexOf('.');
if (pos != -1) {
String extension = filename.substring(pos + 1, filename.length()).toLowerCase();
FileOutputStream out = new FileOutputStream(filename);
boolean written = false;
try {
written = this.write(wrapper, extension, out, quality, alpha);
} finally {
out.close();
}
// if nothing worked, fall back to the Jimi mechanisms and see wether something comes out
if (!written) {
try {
Jimi.putImage(wrapper.getImage(), filename);
} catch (JimiException e) {
throw new IOException(e.getMessage());
}
}
}
}
/**
* Saves the image. Image format is deduced from filename.
*
* @param wrapper ...
* @param out ...
* @param mimeType ...
* @param quality ...
* @param alpha ...
* @throws IOException
* @see helma.image.ImageGenerator#write(helma.image.ImageWrapper, java.io.OutputStream, java.lang.String, float, boolean)
*/
public void write(ImageWrapper wrapper, OutputStream out, String mimeType, float quality, boolean alpha) throws IOException {
// determine the type from the mime type by taking away image/ and image/x-
if (mimeType.startsWith("image/")) {
String type = mimeType.substring(6);
if (type.startsWith("x-"))
type = type.substring(2);
boolean written = false;
try {
written = this.write(wrapper, type, out, quality, alpha);
} finally {
out.close();
}
// if nothing worked, fall back to the Jimi mechanisms and see wether something comes out
if (!written) {
try {
Jimi.putImage(mimeType, wrapper.getImage(), out);
} catch (JimiException e) {
throw new IOException(e.getMessage());
}
}
}
}
}