Solving the quality problem when resizing. It costs a bit in CPU usage, but worth it.

This commit is contained in:
knut 2009-01-12 04:45:49 +00:00
parent 779138efaa
commit 55366a9aa7
3 changed files with 49 additions and 25 deletions

View file

@ -0,0 +1 @@
cache.properties

View file

@ -16,6 +16,11 @@ public class Dimension {
int w; int w;
int h; int h;
public Dimension(Dimension other) {
this.w = other.w;
this.h = other.h;
}
public Dimension(int w, int h) { public Dimension(int w, int h) {
this.w = w; this.w = w;
this.h = h; this.h = h;
@ -28,6 +33,16 @@ public class Dimension {
} }
public Dimension doubled() {
return new Dimension(w * 2, h * 2);
}
public Dimension halved() {
return new Dimension(w / 2, h / 2);
}
public Dimension scaled(int max) { public Dimension scaled(int max) {
return w > h ? scaleWidth(max) : scaleHeight(max); return w > h ? scaleWidth(max) : scaleHeight(max);

View file

@ -153,61 +153,69 @@ public class AlbumServlet
outd = orig.scaled(worh); outd = orig.scaled(worh);
} }
/* In order to make the quality as good as possible we follow the advice from
* http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
* and scale the image by a factor of 2 in intermediate steps, thereby getting a smoother
* end result. The first scale will get to a size which is a multiple of the result size.
* the first scaling operation will also take care of any rotation that needs to happen.
*/
Dimension intermediate = new Dimension(outd);
int targetWidth = orig.getWidth() / 2;
while (intermediate.getWidth() < targetWidth) {
intermediate = intermediate.doubled();
}
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("jpg"); Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("jpg");
ImageReader reader = readers.next(); ImageReader reader = readers.next();
ImageInputStream iis = ImageIO.createImageInputStream(file); ImageInputStream iis = ImageIO.createImageInputStream(file);
reader.setInput(iis, true); reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam(); ImageReadParam param = reader.getDefaultReadParam();
// param.setDestinationType(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB));
if (false /* subsampling */) {
double subSampleScale = (double)outd.getWidth() / orig.getWidth();
int subSampling = (int)Math.floor(0.25d/subSampleScale);
if (subSampling > 1) {
param.setSourceSubsampling(subSampling, subSampling, 0, 0);
}
}
BufferedImage img = reader.read(0, param); BufferedImage img = reader.read(0, param);
// Recalculate scale after sub-sampling was applied // Recalculate scale after sub-sampling was applied
double scale; double scale;
AffineTransform xform; AffineTransform xform;
if (thumbnail.getOrientation() == 6) { if (thumbnail.getOrientation() == 6) {
xform = AffineTransform.getTranslateInstance(outd.getWidth() / 2d, outd.getHeight() / 2d); xform = AffineTransform.getTranslateInstance(intermediate.getWidth() / 2d, intermediate.getHeight() / 2d);
xform.rotate(Math.PI / 2); xform.rotate(Math.PI / 2);
scale = (double)outd.getHeight() / img.getWidth(); scale = (double)intermediate.getHeight() / img.getWidth();
xform.scale(scale, scale); xform.scale(scale, scale);
xform.translate(-img.getWidth() / 2d, -img.getHeight() / 2d); xform.translate(-img.getWidth() / 2d, -img.getHeight() / 2d);
} else { } else {
scale = (double)outd.getWidth() / img.getWidth(); scale = (double)intermediate.getWidth() / img.getWidth();
xform = AffineTransform.getScaleInstance(scale, scale); xform = AffineTransform.getScaleInstance(scale, scale);
} }
// AffineTransformOp operation = new AffineTransformOp(xform, AffineTransformOp.TYPE_BICUBIC);
// BufferedImage buf2 = operation.createCompatibleDestImage(img, img.getColorModel());
BufferedImage buf2 = new BufferedImage(outd.getWidth(), outd.getHeight(), img.getType());
// operation.filter(img, buf2);
BufferedImage buf2 = new BufferedImage(intermediate.getWidth(), intermediate.getHeight(), img.getType());
Graphics2D g2 = buf2.createGraphics(); Graphics2D g2 = buf2.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2.transform(xform); g2.transform(xform);
g2.drawImage(img, 0, 0, null); g2.drawImage(img, 0, 0, null);
while (intermediate.getWidth() > outd.getWidth()) {
BufferedImage buf3 = buf2;
intermediate = intermediate.halved();
buf2 = new BufferedImage(intermediate.getWidth(), intermediate.getHeight(), img.getType());
Graphics2D g3 = buf2.createGraphics();
g3.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g3.drawImage(buf3, 0, 0, intermediate.getWidth(), intermediate.getHeight(), null);
}
// g2.drawImage(img, operation, 0, 0); // g2.drawImage(img, operation, 0, 0);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
ImageWriter writer = writers.next(); ImageWriter writer = writers.next();
ImageWriteParam wParam = writer.getDefaultWriteParam(); // ImageWriteParam wParam = writer.getDefaultWriteParam();
wParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // wParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
wParam.setCompressionQuality(1.0f); // wParam.setCompressionQuality(1.0f);
ImageOutputStream ios = ImageIO.createImageOutputStream(res.getOutputStream()); ImageOutputStream ios = ImageIO.createImageOutputStream(res.getOutputStream());
writer.setOutput(ios); writer.setOutput(ios);
writer.write(null, new IIOImage(buf2, null, null), wParam); // writer.write(null, new IIOImage(buf2, null, null), wParam);
writer.write(buf2);
} }