package play.libs; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.FileImageOutputStream; import jj.play.ns.nl.captcha.backgrounds.BackgroundProducer; import jj.play.ns.nl.captcha.backgrounds.FlatColorBackgroundProducer; import jj.play.ns.nl.captcha.backgrounds.GradiatedBackgroundProducer; import jj.play.ns.nl.captcha.backgrounds.SquigglesBackgroundProducer; import jj.play.ns.nl.captcha.backgrounds.TransparentBackgroundProducer; import jj.play.ns.nl.captcha.gimpy.GimpyRenderer; import jj.play.ns.nl.captcha.gimpy.RippleGimpyRenderer; import jj.play.ns.nl.captcha.noise.CurvedLineNoiseProducer; import jj.play.ns.nl.captcha.text.renderer.DefaultWordRenderer; import play.exceptions.UnexpectedException; import play.mvc.Http.Response; /** * Images utils */ public class Images { /** * Resize an image * @param originalImage The image file * @param to The destination file * @param w The new width (or -1 to proportionally resize) * @param h The new height (or -1 to proportionally resize) */ public static void resize(File originalImage, File to, int w, int h) { resize(originalImage, to, w, h, false); } /** * Resize an image * @param originalImage The image file * @param to The destination file * @param w The new width (or -1 to proportionally resize) or the maxWidth if keepRatio is true * @param h The new height (or -1 to proportionally resize) or the maxHeight if keepRatio is true * @param keepRatio : if true, resize will keep the original image ratio and use w and h as max dimensions */ public static void resize(File originalImage, File to, int w, int h, boolean keepRatio) { try { BufferedImage source = ImageIO.read(originalImage); int owidth = source.getWidth(); int oheight = source.getHeight(); double ratio = (double) owidth / oheight; int maxWidth = w; int maxHeight = h; if (w < 0 && h < 0) { w = owidth; h = oheight; } if (w < 0 && h > 0) { w = (int) (h * ratio); } if (w > 0 && h < 0) { h = (int) (w / ratio); } if(keepRatio) { h = (int) (w / ratio); if(h > maxHeight) { h = maxHeight; w = (int) (h * ratio); } if(w > maxWidth) { w = maxWidth; h = (int) (w / ratio); } } String mimeType = "image/jpeg"; if (to.getName().endsWith(".png")) { mimeType = "image/png"; } if (to.getName().endsWith(".gif")) { mimeType = "image/gif"; } // out BufferedImage dest = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Image srcSized = source.getScaledInstance(w, h, Image.SCALE_SMOOTH); Graphics graphics = dest.getGraphics(); graphics.setColor(Color.WHITE); graphics.fillRect(0, 0, w, h); graphics.drawImage(srcSized, 0, 0, null); ImageWriter writer = ImageIO.getImageWritersByMIMEType(mimeType).next(); ImageWriteParam params = writer.getDefaultWriteParam(); FileImageOutputStream toFs = new FileImageOutputStream(to); writer.setOutput(toFs); IIOImage image = new IIOImage(dest, null, null); writer.write(null, image, params); toFs.flush(); toFs.close(); } catch (Exception e) { throw new RuntimeException(e); } } /** * Crop an image * @param originalImage The image file * @param to The destination file * @param x1 The new x origin * @param y1 The new y origin * @param x2 The new x end * @param y2 The new y end */ public static void crop(File originalImage, File to, int x1, int y1, int x2, int y2) { try { BufferedImage source = ImageIO.read(originalImage); String mimeType = "image/jpeg"; if (to.getName().endsWith(".png")) { mimeType = "image/png"; } if (to.getName().endsWith(".gif")) { mimeType = "image/gif"; } int width = x2 - x1; int height = y2 - y1; // out BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Image croppedImage = source.getSubimage(x1, y1, width, height); Graphics graphics = dest.getGraphics(); graphics.setColor(Color.WHITE); graphics.fillRect(0, 0, width, height); graphics.drawImage(croppedImage, 0, 0, null); ImageWriter writer = ImageIO.getImageWritersByMIMEType(mimeType).next(); ImageWriteParam params = writer.getDefaultWriteParam(); writer.setOutput(new FileImageOutputStream(to)); IIOImage image = new IIOImage(dest, null, null); writer.write(null, image, params); } catch (Exception e) { throw new RuntimeException(e); } } /** * Encode an image to base64 using a data: URI * @param image The image file * @return The base64 encoded value * @throws java.io.IOException */ public static String toBase64(File image) throws IOException { return "data:" + MimeTypes.getMimeType(image.getName()) + ";base64," + Codec.encodeBASE64(IO.readContent(image)); } /** * Create a captche image */ public static Captcha captcha(int width, int height) { return new Captcha(width, height); } /** * Create a 150x150 captcha image */ public static Captcha captcha() { return captcha(150, 50); } /** * A captcha image. */ public static class Captcha extends InputStream { public String text = null; public BackgroundProducer background = new TransparentBackgroundProducer(); public GimpyRenderer gimpy = new RippleGimpyRenderer(); public Color textColor = Color.BLACK; public List<Font> fonts = new ArrayList<Font>(2); public int w, h; public Color noise = null; public Captcha(int w, int h) { this.w = w; this.h = h; this.fonts.add(new Font("Arial", Font.BOLD, 40)); this.fonts.add(new Font("Courier", Font.BOLD, 40)); } /** * Tell the captche to draw a text and retrieve it */ public String getText() { return getText(5); } /** * Tell the captche to draw a text using the specified color (ex. #000000) and retrieve it */ public String getText(String color) { this.textColor = Color.decode(color); return getText(); } /** * Tell the captche to draw a text of the specified size and retrieve it */ public String getText(int length) { return getText(length, "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"); } /** * Tell the captche to draw a text of the specified size using the specified color (ex. #000000) and retrieve it */ public String getText(String color, int length) { this.textColor = Color.decode(color); return getText(length); } public String getText(int length, String chars) { char[] charsArray = chars.toCharArray(); Random random = new Random(System.currentTimeMillis()); StringBuffer sb = new StringBuffer(length); for (int i = 0; i < length; i++) { sb.append(charsArray[random.nextInt(charsArray.length)]); } text = sb.toString(); return text; } public String getText(String color, int length, String chars) { this.textColor = Color.decode(color); return getText(length, chars); } /** * Add noise to the captcha. */ public Captcha addNoise() { noise = Color.BLACK; return this; } /** * Add noise to the captcha. */ public Captcha addNoise(String color) { noise = Color.decode(color); return this; } /** * Set a gradient background. */ public Captcha setBackground(String from, String to) { GradiatedBackgroundProducer bg = new GradiatedBackgroundProducer(); bg.setFromColor(Color.decode(from)); bg.setToColor(Color.decode(to)); background = bg; return this; } /** * Set a solid background. */ public Captcha setBackground(String color) { background = new FlatColorBackgroundProducer(Color.decode(color)); return this; } /** * Set a squiggles background */ public Captcha setSquigglesBackground() { background = new SquigglesBackgroundProducer(); return this; } // ~~ rendering ByteArrayInputStream bais = null; @Override public int read() throws IOException { check(); return bais.read(); } @Override public int read(byte[] b) throws IOException { check(); return bais.read(b); } void check() { try { if (bais == null) { if (text == null) { text = getText(); } BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); DefaultWordRenderer renderer = new DefaultWordRenderer(textColor, fonts); bi = background.addBackground(bi); renderer.render(text, bi); if (noise != null) { new CurvedLineNoiseProducer(noise, 3.0f).makeNoise(bi); } gimpy.gimp(bi); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(bi, "png", baos); bais = new ByteArrayInputStream(baos.toByteArray()); // Response.current().contentType = "image/png"; } } catch (Exception e) { throw new UnexpectedException(e); } } } }