diff --git a/photos/geiranger/.cvsignore b/photos/geiranger/.cvsignore new file mode 100644 index 0000000..ec109f1 --- /dev/null +++ b/photos/geiranger/.cvsignore @@ -0,0 +1 @@ +cache.properties diff --git a/src/org/forkalsrud/album/exif/Dimension.java b/src/org/forkalsrud/album/exif/Dimension.java index 276569d..b14c231 100644 --- a/src/org/forkalsrud/album/exif/Dimension.java +++ b/src/org/forkalsrud/album/exif/Dimension.java @@ -16,6 +16,11 @@ public class Dimension { int w; int h; + public Dimension(Dimension other) { + this.w = other.w; + this.h = other.h; + } + public Dimension(int w, int h) { this.w = w; 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) { return w > h ? scaleWidth(max) : scaleHeight(max); diff --git a/src/org/forkalsrud/album/web/AlbumServlet.java b/src/org/forkalsrud/album/web/AlbumServlet.java index f061df8..b778fd6 100644 --- a/src/org/forkalsrud/album/web/AlbumServlet.java +++ b/src/org/forkalsrud/album/web/AlbumServlet.java @@ -153,61 +153,69 @@ public class AlbumServlet 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 readers = ImageIO.getImageReadersByFormatName("jpg"); ImageReader reader = readers.next(); ImageInputStream iis = ImageIO.createImageInputStream(file); reader.setInput(iis, true); 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); // Recalculate scale after sub-sampling was applied double scale; AffineTransform xform; 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); - scale = (double)outd.getHeight() / img.getWidth(); + scale = (double)intermediate.getHeight() / img.getWidth(); xform.scale(scale, scale); xform.translate(-img.getWidth() / 2d, -img.getHeight() / 2d); } else { - scale = (double)outd.getWidth() / img.getWidth(); + scale = (double)intermediate.getWidth() / img.getWidth(); 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(); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - 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.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2.transform(xform); g2.drawImage(img, 0, 0, null); -// g2.drawImage(img, operation, 0, 0); + 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); Iterator writers = ImageIO.getImageWritersByFormatName("jpg"); ImageWriter writer = writers.next(); - ImageWriteParam wParam = writer.getDefaultWriteParam(); - wParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); - wParam.setCompressionQuality(1.0f); +// ImageWriteParam wParam = writer.getDefaultWriteParam(); +// wParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); +// wParam.setCompressionQuality(1.0f); ImageOutputStream ios = ImageIO.createImageOutputStream(res.getOutputStream()); writer.setOutput(ios); - writer.write(null, new IIOImage(buf2, null, null), wParam); +// writer.write(null, new IIOImage(buf2, null, null), wParam); + writer.write(buf2); }