Index: trunk/grails-app/utils/Imaging.groovy
===================================================================
--- trunk/grails-app/utils/Imaging.groovy	(revision 182)
+++ trunk/grails-app/utils/Imaging.groovy	(revision 182)
@@ -0,0 +1,149 @@
+import java.awt.AlphaComposite
+import java.awt.Color
+import java.awt.Graphics2D
+import java.awt.geom.AffineTransform
+import java.awt.geom.Rectangle2D
+import java.awt.image.AffineTransformOp
+import java.awt.image.BufferedImage
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import javax.imageio.ImageIO
+
+class Imaging {
+
+    static def createAll(InventoryItem inventoryItem, Picture picture, InputStream stream) {
+        BufferedImage original = ImageIO.read(stream)
+        picture.contentType = 'image/jpeg'
+        picture.width = original.width
+        picture.height = original.height
+        def images = [ 
+            new Image(inventoryItem: inventoryItem, picture: picture, size: Image.Original),
+            new Image(inventoryItem: inventoryItem, picture: picture, size: Image.Large),
+            new Image(inventoryItem: inventoryItem, picture: picture, size: Image.Medium),
+            new Image(inventoryItem: inventoryItem, picture: picture, size: Image.Small)
+            ]
+        ByteArrayOutputStream output = new ByteArrayOutputStream(1024 * 1024)
+        updateImages(images, original, output)
+        images
+    }
+    
+    static def updateAll(Picture picture) {
+        def operation = picture.operation
+        if (operation == Picture.NoOp) {
+            return null
+        }
+        def images = picture.images.toArray() // Image.findAllByPicture(picture, [ sort: 'size', order: 'asc' ])
+        BufferedImage original = ImageIO.read(new ByteArrayInputStream(images[0].data))
+        AffineTransform transform = new AffineTransform();
+        def op = null
+        boolean paint = false
+        Integer width = original.width
+        Integer height = original.height
+        switch (operation) {
+            case Picture.RotateClockWise90:
+                transform.rotate(Math.PI / 2.0)
+                transform.translate(0, -height)
+                op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
+                width = original.height
+                height = original.width
+                paint = true
+                break
+            case Picture.RotateAntiClockWise90:
+                transform.rotate(-Math.PI / 2.0)
+                transform.translate(-width, 0)
+                op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
+                width = original.height
+                height = original.width
+                paint = true
+                break
+            case Picture.Rotate180:
+                transform.rotate(Math.PI)
+                transform.translate(-width, -height)
+                op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
+                break
+            case Picture.Flip: // vertical
+                transform.scale(-1.0d, 1.0d)
+                transform.translate(-width, 0)
+                op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR)
+                break
+            case Picture.Flop: // horizontal
+                transform.scale(1.0d, -1.0d)
+                transform.translate(0, -height)
+                op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR)
+                break
+            default:
+                return images
+                break
+        }
+        BufferedImage modified = op.filter(original, null);
+        if (paint) {
+            BufferedImage changed = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
+            Graphics2D graphics = changed.createGraphics()
+            graphics.drawImage(modified, 0, 0, width, height, null)
+            graphics.dispose()
+            modified = changed
+            picture.width = width
+            picture.height = height
+        }
+        ByteArrayOutputStream output = new ByteArrayOutputStream(1024 * 1024)
+        updateImages(images, modified, output)
+        images
+    }
+
+    private static def updateImages(images, original, stream) {
+        updateImage(images[0], original, 'jpeg', stream)
+        def large = resizeImage(original, dimensions(Image.Large), false)
+        updateImage(images[1], large, 'png', stream)
+        updateImage(images[2], resizeImage(large, dimensions(Image.Medium), true), 'png', stream)
+        updateImage(images[3], resizeImage(large, dimensions(Image.Small), true), 'png', stream)
+    }
+
+    private static def updateImage(image, data, format, stream) {
+        image.contentType = "image/${format}"
+        image.width = data.width
+        image.height = data.height
+        stream.reset()
+        if (!ImageIO.write(data, format, stream)) {
+            throw new IOException("Can't write the image in the given format '${format}'")
+        }
+        image.data = stream.toByteArray()
+    }
+
+    private static def dimensions(size) {
+        [ Image.Widths[size], Image.Heights[size] ]
+    }    
+
+    private static def resizeImage(imageBuffer, dims, fit) {
+        Integer width = dims[0]
+        Integer height = dims[1]
+        Integer imageWidth = imageBuffer.width
+        Integer imageHeight = imageBuffer.height
+      
+        Double widthScale = (double)width / (double)imageWidth
+        Double heightScale = (double)height / (double)imageHeight
+        BufferedImage resizedImage = imageBuffer
+        if (widthScale < 1.0d || heightScale < 1.0d) {
+            Double scale = Math.min(widthScale, heightScale)
+            def transform = new AffineTransform()
+            transform.scale(scale, scale)
+            def op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR)
+            resizedImage = op.filter(imageBuffer, null)
+            imageWidth = resizedImage.width
+            imageHeight = resizedImage.height
+        }
+
+        if (fit && (imageWidth < width || imageHeight < height)) {
+            BufferedImage fittedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
+            Integer left = (width - imageWidth) / 2
+            Integer top = (height - imageHeight) / 2
+            Graphics2D graphics = fittedImage.createGraphics()
+            graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f))
+            graphics.fill(new Rectangle2D.Double(0, 0, width, height))
+            graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f))
+            graphics.drawImage(resizedImage, left, top, imageWidth, imageHeight, null)
+            graphics.dispose()
+            return fittedImage
+        }
+        resizedImage
+    }
+}
