Received: from PACIFIC-CARRIER-ANNEX.MIT.EDU by po10.MIT.EDU (5.61/4.7) id AA02455; Fri, 22 Oct 99 00:45:25 EDT Received: from hermes.javasoft.com by MIT.EDU with SMTP id AA11426; Fri, 22 Oct 99 00:45:30 EDT Received: (from nobody@localhost) by hermes.java.sun.com (8.9.3+Sun/8.9.1) id EAA02365; Fri, 22 Oct 1999 04:45:15 GMT Date: Fri, 22 Oct 1999 04:45:15 GMT Message-Id: <199910220445.EAA02365@hermes.java.sun.com> X-Authentication-Warning: hermes.java.sun.com: Processed from queue /bulkmail/data/ed_92/mqueue1 X-Mailing: 191 From: JDCTechTips@sun.com Subject: JDC Tech Tips Oct. 21, 1999 To: JDCMember@sun.com Reply-To: JDCTechTips@sun.com Errors-To: bounced_mail@hermes.java.sun.com Precedence: junk Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Mailer: Beyond Email 2.2 J D C T E C H T I P S TIPS, TECHNIQUES, AND SAMPLE CODE WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips, October 21, 1999. This issue covers: * Creating Image Thumbnails * Capturing Standard Output in a Log File This issue of the JDC Tech Tips is written by Patrick Chan, author of "The Java(tm) Developers Almanac" (http://www.amazon.com/exec/obidos/ASIN/0201432986/xeo). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CREATING IMAGE THUMBNAILS This tip demonstrates how you can create a small thumbnail version of a larger image. This is especially important for web applications that need to display a number of images on a page and do it in a reasonable amount of time. The tip applies to the Java(tm) 2 Platform, JDK(tm) v1.2 and uses the com.sun.image.codec.jpeg package. Although this package is currently included in JDK v1.2, it's not yet part of the Java 2 Platform APIs and so it might change or be deleted in the future. The example program below illustrates how to create an image thumbnail. The program takes any GIF or JPEG file and creates a smaller JPEG file. The program shrinks the original image so that it fits into a square whose size you specify. In other words, the program maintains the aspect ratio, that is, the width-to-height ratio, of the original image. It also ensures that the width and height of the thumbnail are less than the size you specify. Here's the code for the example program. import java.awt.Image; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.io.FileOutputStream; import javax.swing.ImageIcon; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; class Thumbnail { public static void main(String[] args) { createThumbnail(args[0], args[1], Integer.parseInt(args[2])); } /** * Reads an image in a file and creates a thumbnail in another file. * @param orig The name of image file. * @param thumb The name of thumbnail file. Will be created if necessary. * @param maxDim The width and height of the thumbnail must * be maxDim pixels or less. */ public static void createThumbnail(String orig, String thumb, int maxDim) { try { // Get the image from a file. Image inImage = new ImageIcon(orig).getImage(); // Determine the scale. double scale = (double)maxDim/(double)inImage.getHeight(null); if (inImage.getWidth(null) > inImage.getHeight(null)) { scale = (double)maxDim/(double)inImage.getWidth(null); } // Determine size of new image. One of them // should equal maxDim. int scaledW = (int)(scale*inImage.getWidth(null)); int scaledH = (int)(scale*inImage.getHeight(null)); // Create an image buffer in which to paint on. BufferedImage outImage = new BufferedImage(scaledW, scaledH, BufferedImage.TYPE_INT_RGB); // Set the scale. AffineTransform tx = new AffineTransform(); // If the image is smaller than the desired image size, // don't bother scaling. if (scale < 1.0d) { tx.scale(scale, scale); } // Paint image. Graphics2D g2d = outImage.createGraphics(); g2d.drawImage(inImage, tx, null); g2d.dispose(); // JPEG-encode the image and write to file. OutputStream os = new FileOutputStream(thumb); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os); encoder.encode(outImage); os.close(); } catch (IOException e) { e.printStackTrace(); } System.exit(0); } } You run the program like this: java Thumbnail Notice that the program takes three parameters. The first parameter is the name of the file for the source image. The second parameter is the name of the file for the resulting image thumbnail. The third parameter specifies the maximum number of pixels for the width and height of the thumbnail. The width and height of the thumbnail will be no larger than the value of maxDim. The width or height of the thumbnail can be smaller than maxDim if the width or height of the original image is smaller. The program first loads the image file using the ImageIcon class. This class differs from Toolkit.getImage() in that ImageIcon doesn't return until the image is fully loaded. The image needs to be fully loaded in order to immediately access its width and height. With the width and height, the program can determine the scaling factor. This means how much the image needs to be reduced so that it can fit into a square of maxDim pixels. Both the width and height of the image are scaled down by the same amount to maintain the aspect ratio. The program scales the image. It creates a transform (AffineTransform) object to do the scaling and an image buffer to hold the new scaled image. Using the scale factor, the program can determine the exact size of the thumbnail image and the size of an image buffer just large enough to hold it. The transform is set with the scaling factor. However, no scaling takes place if the original image is smaller than the thumbnail. After scaling, the program paints the original image onto the new image buffer. It does this by creating a graphic context on the image buffer and then painting the original image using the graphics context. Now the thumbnail is in image buffer. The final step is to convert the image buffer into a JPEG stream to be written to a file. This is done using the JPEGImageEncoder.encode() method. CAPTURING STANDARD OUTPUT IN A LOG FILE This tip demonstrates how you can record in a log file everything you print to standard out and standard error. This is especially useful if you deploy an application and your users encounter problems. You can have the users send you the log file for analysis. The following example code demonstrates how to capture standard output. You can include the example code, as is, in your program. The example implements a class called SaveOutput, with two static methods - start() and stop(). Calling start() creates a new log file or empties an existing log file. It copies into the log file characters printed to standard output and standard error. Calling stop() closes the log file and restores the behavior of standard output and standard error (that is, their behavior before start() was called). import java.io.*; class Stdout { public static void main(String[] args) { try { // Start capturing characters into the log file. SaveOutput.start("log.txt"); // Test it. System.out.println("Here's is some stuff to stdout."); System.err.println("Here's is some stuff to stderr."); System.out.println("Let's throw an exception..."); new Exception().printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { // Stop capturing characters into the log file // and restore old setup. SaveOutput.stop(); } } } class SaveOutput extends PrintStream { static OutputStream logfile; static PrintStream oldStdout; static PrintStream oldStderr; SaveOutput(PrintStream ps) { super(ps); } // Starts copying stdout and stderr to the file f. public static void start(String f) throws IOException { // Save old settings. oldStdout = System.out; oldStderr = System.err; // Create/Open logfile. logfile = new PrintStream( new BufferedOutputStream( new FileOutputStream(f))); // Start redirecting the output. System.setOut(new SaveOutput(System.out)); System.setErr(new SaveOutput(System.err)); } // Restores the original settings. public static void stop() { System.setOut(oldStdout); System.setErr(oldStderr); try { logfile.close(); } catch (Exception e) { e.printStackTrace(); } } // PrintStream override. public void write(int b) { try { logfile.write(b); } catch (Exception e) { e.printStackTrace(); setError(); } super.write(b); } // PrintStream override. public void write(byte buf[], int off, int len) { try { logfile.write(buf, off, len); } catch (Exception e) { e.printStackTrace(); setError(); } super.write(buf, off, len); } } The start() method first saves the current standard output and standard error print streams. These print streams will be restored when stop() is called. Next, the log file is opened. If the log file does not exist, it's created. Otherwise, the log file is emptied. Finally, System.setOut() and System.setErr() are called to replace the standard output and standard error print streams with SaveOutput print streams. The stop () method restores the original standard output and standard error. It then closes the log file. A SaveOutput object is a PrintStream object that acts like a tee. Any characters it receives are forwarded to two places: the log file and the underlying print streams. The underlying print streams in the example are the original standard output and standard error print streams; these are supplied to the SaveOutput constructor. Although both standard output and standard error are written into the same logfile, there is no need to synchronize this operation. The reason is that the logfile output stream is itself a print stream and write operations are synchronized. To implement this tee behavior, the program needs to override the two forms of the write method. These overrides simply write the characters into the logfile and then to the underlying print stream (by calling super.write()). The write() methods do not throw exceptions. Instead, they set a flag in the print stream if some problem occurs. They set the flag by calling setError(). If the client of the print stream wants to check if an error occurred, it can call checkError(). . . . . . . . . . . . . . . . . . . . . . . . . - NOTE The names on the JDC mailing list are used for internal Sun Microsystems(tm) purposes only. To remove your name from the list, see Subscribe/Unsubscribe below. - FEEDBACK Comments? Send your feedback on the JDC Tech Tips to: jdc-webmaster@java.sun.com - SUBSCRIBE/UNSUBSCRIBE The JDC Tech Tips are sent to you because you elected to subscribe when you registered as a JDC member. To unsubscribe from JDC Email, go to the following address and enter the email address you wish to remove from the mailing list: http://developer.java.sun.com/unsubscribe.html To become a JDC member and subscribe to this newsletter go to: http://java.sun.com/jdc/ - ARCHIVES You'll find the JDC Tech Tips archives at: http://developer.java.sun.com/developer/TechTips/index.html - COPYRIGHT Copyright 1999 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA. This document is protected by copyright. For more information, see: http://developer.java.sun.com/developer/copyright.html JDC Tech Tips October 21, 1999