Java tutorial
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; /* * ScaleTest_2008.java * * Created on May 1, 2007, 4:42 PM * * Copyright (c) 2007, Sun Microsystems, Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the TimingFramework project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * * @author Chet */ public class ScaleTest_2008 extends JComponent { private static final int FULL_SIZE = 190; private static final int PADDING = 5; private static final int QUAD_SIZE = FULL_SIZE / 2; private static final double SCALE_FACTOR = .17; private static BufferedImage originalImage = new BufferedImage(FULL_SIZE, FULL_SIZE, BufferedImage.TYPE_INT_RGB); boolean originalImagePainted = false; /** * Paints the test image that will be downscaled and timed by the various * scaling methods. A different image is rendered into each of the four * quadrants of this image: RGB stripes, a picture, vector art, and * a black and white grid. */ private void paintOriginalImage() { Graphics g = originalImage.getGraphics(); // Erase to black g.setColor(Color.BLACK); g.fillRect(0, 0, FULL_SIZE, FULL_SIZE); // RGB quadrant for (int i = 0; i < QUAD_SIZE; i += 3) { int x = i; g.setColor(Color.RED); g.drawLine(x, 0, x, QUAD_SIZE); x++; g.setColor(Color.GREEN); g.drawLine(x, 0, x, QUAD_SIZE); x++; g.setColor(Color.BLUE); g.drawLine(x, 0, x, QUAD_SIZE); } // Picture quadrant try { URL url = getClass().getResource("BBGrayscale.png"); BufferedImage picture = ImageIO.read(url); // Center picture in quadrant area int xDiff = QUAD_SIZE - picture.getWidth(); int yDiff = QUAD_SIZE - picture.getHeight(); g.drawImage(picture, QUAD_SIZE + xDiff / 2, yDiff / 2, null); } catch (Exception e) { System.out.println("Problem reading image file: " + e); } // Vector drawing quadrant g.setColor(Color.WHITE); g.fillRect(0, QUAD_SIZE, QUAD_SIZE, QUAD_SIZE); g.setColor(Color.BLACK); g.drawOval(2, QUAD_SIZE + 2, QUAD_SIZE - 4, QUAD_SIZE - 4); g.drawArc(20, QUAD_SIZE + 20, (QUAD_SIZE - 40), QUAD_SIZE - 40, 190, 160); int eyeSize = 7; int eyePos = 30 - (eyeSize / 2); g.fillOval(eyePos, QUAD_SIZE + eyePos, eyeSize, eyeSize); g.fillOval(QUAD_SIZE - eyePos - eyeSize, QUAD_SIZE + eyePos, eyeSize, eyeSize); // B&W grid g.setColor(Color.WHITE); g.fillRect(QUAD_SIZE + 1, QUAD_SIZE + 1, QUAD_SIZE, QUAD_SIZE); g.setColor(Color.BLACK); for (int i = 0; i < QUAD_SIZE; i += 4) { int pos = QUAD_SIZE + i; g.drawLine(pos, QUAD_SIZE + 1, pos, FULL_SIZE); g.drawLine(QUAD_SIZE + 1, pos, FULL_SIZE, pos); } originalImagePainted = true; } /** * Progressive bilinear scaling: for any downscale size, scale * iteratively by halves using BILINEAR filtering until the proper * size is reached. */ private BufferedImage getOptimalScalingImage(BufferedImage inputImage, int startSize, int endSize) { int currentSize = startSize; BufferedImage currentImage = inputImage; int delta = currentSize - endSize; int nextPow2 = currentSize >> 1; while (currentSize > 1) { if (delta <= nextPow2) { if (currentSize != endSize) { BufferedImage tmpImage = new BufferedImage(endSize, endSize, BufferedImage.TYPE_INT_RGB); Graphics g = tmpImage.getGraphics(); ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(currentImage, 0, 0, tmpImage.getWidth(), tmpImage.getHeight(), null); currentImage = tmpImage; } return currentImage; } else { BufferedImage tmpImage = new BufferedImage(currentSize >> 1, currentSize >> 1, BufferedImage.TYPE_INT_RGB); Graphics g = tmpImage.getGraphics(); ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(currentImage, 0, 0, tmpImage.getWidth(), tmpImage.getHeight(), null); currentImage = tmpImage; currentSize = currentImage.getWidth(); delta = currentSize - endSize; nextPow2 = currentSize >> 1; } } return currentImage; } /** * Progressive Bilinear approach: this method gets each scaled version from * the getOptimalScalingImage method and copies it into place. */ private void drawBetterImage(Graphics g, int yLoc) { int xLoc = 100; int delta = (int) (SCALE_FACTOR * FULL_SIZE); for (int scaledSize = FULL_SIZE; scaledSize > 0; scaledSize -= delta) { Image scaledImage = getOptimalScalingImage(originalImage, FULL_SIZE, scaledSize); g.drawImage(scaledImage, xLoc, yLoc + (FULL_SIZE - scaledSize) / 2, null); xLoc += scaledSize + 20; } } /** * This approach uses either the getScaledInstance() approach to get * each new size or it scales on the fly using drawImage(). */ private void drawImage(Graphics g, int yLoc, boolean getScaled) { int xLoc = 100; int delta = (int) (SCALE_FACTOR * FULL_SIZE); if (getScaled) { for (int scaledSize = FULL_SIZE; scaledSize > 0; scaledSize -= delta) { Image scaledImage = originalImage.getScaledInstance(scaledSize, scaledSize, Image.SCALE_AREA_AVERAGING); g.drawImage(scaledImage, xLoc, yLoc + (FULL_SIZE - scaledSize) / 2, null); xLoc += scaledSize + 20; } } else { for (int scaledSize = FULL_SIZE; scaledSize > 0; scaledSize -= delta) { g.drawImage(originalImage, xLoc, yLoc + (FULL_SIZE - scaledSize) / 2, scaledSize, scaledSize, null); xLoc += scaledSize + 20; } } } /** * Scale the image to several smaller sizes using each of the approaches * and time each series of operations. The times are output into the * application window for each row that they represent. */ protected void paintComponent(Graphics g) { if (!originalImagePainted) { paintOriginalImage(); } long startTime, endTime, totalTime; int xLoc, yLoc; // Draw scaled versions with nearest neighbor xLoc = 5; yLoc = 20; startTime = System.nanoTime(); drawImage(g, yLoc, false); endTime = System.nanoTime(); totalTime = (endTime - startTime) / 1000000; g.drawString("NEAREST ", xLoc, yLoc + (FULL_SIZE / 2)); g.drawString(Long.toString(totalTime) + " ms", xLoc, yLoc + (FULL_SIZE / 2) + 15); System.out.println("NEAREST: " + (endTime - startTime) / 1000000); // BILINEAR yLoc += FULL_SIZE + PADDING; ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); startTime = System.nanoTime(); drawImage(g, yLoc, false); endTime = System.nanoTime(); totalTime = (endTime - startTime) / 1000000; g.drawString("BILINEAR ", xLoc, yLoc + (FULL_SIZE / 2)); g.drawString(Long.toString(totalTime) + " ms", xLoc, yLoc + (FULL_SIZE / 2) + 15); System.out.println("BILINEAR: " + (endTime - startTime) / 1000000); // BIDUBIC yLoc += FULL_SIZE + PADDING; ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); startTime = System.nanoTime(); drawImage(g, yLoc, false); endTime = System.nanoTime(); totalTime = (endTime - startTime) / 1000000; g.drawString("BICUBIC ", xLoc, yLoc + (FULL_SIZE / 2)); g.drawString(Long.toString(totalTime) + " ms", xLoc, yLoc + (FULL_SIZE / 2) + 15); System.out.println("BICUBIC: " + (endTime - startTime) / 1000000); // getScaledInstance() yLoc += FULL_SIZE + PADDING; ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); startTime = System.nanoTime(); drawImage(g, yLoc, true); endTime = System.nanoTime(); totalTime = (endTime - startTime) / 1000000; g.drawString("getScaled ", xLoc, yLoc + (FULL_SIZE / 2)); g.drawString(Long.toString(totalTime) + " ms", xLoc, yLoc + (FULL_SIZE / 2) + 15); System.out.println("getScaled: " + (endTime - startTime) / 1000000); // Progressive Bilinear yLoc += FULL_SIZE + PADDING; startTime = System.nanoTime(); drawBetterImage(g, yLoc); endTime = System.nanoTime(); totalTime = (endTime - startTime) / 1000000; g.drawString("Progressive ", xLoc, yLoc + (FULL_SIZE / 2)); g.drawString(Long.toString(totalTime) + " ms", xLoc, yLoc + (FULL_SIZE / 2) + 15); System.out.println("faster: " + (endTime - startTime) / 1000000); // Draw image sizes xLoc = 100; int delta = (int) (SCALE_FACTOR * FULL_SIZE); for (int scaledSize = FULL_SIZE; scaledSize > 0; scaledSize -= delta) { g.drawString(scaledSize + " x " + scaledSize, xLoc + Math.max(0, scaledSize / 2 - 20), 15); xLoc += scaledSize + 20; } } private static void createAndShowGUI() { JFrame f = new JFrame(); f.setLayout(new BorderLayout()); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(900, 50 + (5 * FULL_SIZE) + (6 * PADDING)); ScaleTest_2008 test = new ScaleTest_2008(); f.add(test); f.setVisible(true); } public static void main(String args[]) { Runnable doCreateAndShowGUI = new Runnable() { public void run() { createAndShowGUI(); } }; SwingUtilities.invokeLater(doCreateAndShowGUI); } }