// JavaPixelCalculator.java // A Java applet that displays an image in the // style of the METIP Pixel Calculator. // Currently set up for Java 2.0 only. // The drawImage call is 1.1 dependent but could // be replaced by some code that scaled the // displayed portion of the image and mapped it // to the screen. // (The 1.1 runtime seems to have a problem // because it won't work correctly if the // applet canvas is larger than about half the // screen.) // (On the other hand, running as an app on a K6 at 333MHz // with the Java 2.0 system, this program does // quite nicely.) // Note applet mode works OK with the Java sdk applet viewer, // But image and pixel numeric values not displayed when applet is run under // Netscape Communicator 4.5. // Next thing to try would be the Java plug-in for Netscape. // Steven L. Tanimoto, 28 October 1997. // Last updated 1 April 1999. // To do: Make sure scroll bar values get updated when the user changes the focus point // by clicking on the image. import java.applet.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.util.*; import java.net.*; class ImageCanvas extends Canvas { Image im; int thePixels[]; ViewImage theView; ColorModel defaultRGBmodel; int canvasWidth, canvasHeight; int imageNCOLS, imageNROWS; int imageFocusX; // coords of where user is looking in the image. int imageFocusY; int imageMouseX, imageMouseY; //coords of mouse cursor. int xdp1, ydp1, xdp2, ydp2; // displayed portion of image. int xoa1, yoa1, xoa2, yoa2; // occupied area of window // (where the displayed portion is shown). double s; // scale factor = // width of an image pixel in the display window. int intScale; // integer version of s for use in // drawing numbers at larger scales. double b, d; // translation parameters in // image-to-window mapping. boolean showNumbers = true; boolean rgb; // true means show 3 color components. int currentScale; // = log base 2 of the ratio of // screen pixel widths to image pixel widths. public ImageCanvas(ViewImage c) { theView = c; addMouseListener (new MyMouseListener()); addMouseMotionListener (new MyMouseMotionListener()); } public void setCanvasInfo(Image img, int imgNCOLS, int imgNROWS, int imgFocusX, int imgFocusY, int thePix[], int curScale, boolean theRgb) { im = img; imageNCOLS = imgNCOLS; imageNROWS = imgNROWS; imageFocusX = imgFocusX; imageFocusY = imgFocusY; thePixels = thePix; currentScale = curScale; rgb = theRgb; } public void paint(Graphics g) { System.out.println("Entering ImageCanvas.paint"); canvasWidth = size().width; canvasHeight = size().height; computeProjectionRectangles(); g.setColor(Color.gray); g.fillRect(0,0,canvasWidth, canvasHeight); g.drawImage(im, xoa1, yoa1, xoa2, yoa2, xdp1, ydp1, xdp2, ydp2, this); if ((currentScale > 3) && showNumbers) { Font font = new Font("Helvetica", Font.PLAIN, (intScale * 2) / 6); FontMetrics fm = g.getFontMetrics(font); int h = fm.getHeight(); g.setFont(font); if (rgb) { g.setColor(Color.red); for (int row = ydp1; row < ydp2; row++) { int redHeight = mapy(row) + h; for (int col = xdp1; col < xdp2; col++) { int theValue = 0xff & ((thePixels[(row * imageNCOLS) + col]) >> 16); //blue component of pixel; g.drawString((new Integer(theValue)).toString(), mapx(col), redHeight); } } g.setColor(Color.green); for (int row = ydp1; row < ydp2; row++) { int greenHeight = mapy(row) + ((h * 5)/ 3); for (int col = xdp1; col < xdp2; col++) { int theValue = 0xff & ((thePixels[(row * imageNCOLS) + col]) >> 8); //blue component of pixel; g.drawString((new Integer(theValue)).toString(), mapx(col), greenHeight); } } g.setColor(Color.blue); for (int row = ydp1; row < ydp2; row++) { int blueHeight = mapy(row) + ((h * 7)/3); for (int col = xdp1; col < xdp2; col++) { int theValue = 0xff & (thePixels[(row * imageNCOLS) + col]); //blue component of pixel; g.drawString((new Integer(theValue)).toString(), mapx(col), blueHeight); } } } else { // show gray values g.setColor(Color.green); for (int row = ydp1; row < ydp2; row++) { int grayHeight = mapy(row) + ((3 * h)/2); for (int col = xdp1; col < xdp2; col++) { int theRGBValue = thePixels[(row * imageNCOLS) + col]; int theGrayValue = ((theRGBValue & 0xff) + ((theRGBValue >>= 8) & 0xff) + ((theRGBValue >> 8) & 0xff)) / 3; g.drawString((new Integer(theGrayValue)).toString(), mapx(col), grayHeight); } } } } } public void update(Graphics g) { paint(g); } public void computeProjectionRectangles() { /* The linear mapping between image points and window points is as follows: xwindow = a * ximage + b ywindow = c * yimage + d We assume uniform scaling in x and y, so that a = c. Let s = a = c. We store scaling information with currentScale which is log base 2 of s. Thus s = 2^{currentScale} There are 5 rectangles of interest: -- the image rectangle (0, 0, imageNCOLS, imageNROWS).; -- the window rectangle (0, 0, windowWidth, windowHeight); -- the bounding area of the displayed portion of the image which is the window rectangle inverse-mapped to the image space; (bx1, by1, bx2, by2); -- the displayed portion of the image, which is the intersection of the bounding area with the image area. (xdp1, ydp1, xdp2, ydp2); -- the occupied area of the window (xoa1, yoa1, xoa2, yoa2) This function computes the last two rectangles from the first two, together with the current scaling factor and the image focus point. */ // Determine the translation parameters of the linear map from image to window: // First compute s from currentScale. // If Java were simpler this would be expressed s=2^currentScale. if (currentScale > 0) s = (new Integer(1 << currentScale)).doubleValue(); else s = 1.0 / (new Integer(1 << -currentScale)).doubleValue(); intScale = (new Float(s)).shortValue(); System.out.println("Scale factor s is " + s); b = (canvasWidth / 2) - (s * imageFocusX); d = (canvasHeight / 2) - (s * imageFocusY); System.out.println("Horizontal translation is " + b); // To compute the displayed region of the image, first map the corners of the window // back into the image space to obtain the "bounding area of displayed portion". long bx1, by1, bx2, by2; // coordinates of the bounding area of the displayed portion of the image. bx1 = (new Float((0 - b) / s)).shortValue(); by1 = (new Float((0 - d) / s)).shortValue(); bx2 = (new Float((canvasWidth - b) / s)).shortValue(); by2 = (new Float((canvasHeight - d) / s)).shortValue(); // Adjust the boundary region by one pixel to make sure fractional parts of pixels get shown. bx1--; by1--; bx2++; by2++; // Find the intersection of the image's area and the bounding area of displayed portion. // This is the rectangle we can call the "displayed portion" of the image. xdp1 = (int) Math.max(0, bx1); ydp1 = (int) Math.max(0, by1); xdp2 = (int) Math.min(imageNCOLS, bx2); ydp2 = (int) Math.min(imageNROWS, by2); // Now find the corresponding rectangle in the window, the "occupied area": xoa1 = (new Float((s * xdp1) + b)).shortValue(); yoa1 = (new Float((s * ydp1) + d)).shortValue(); xoa2 = (new Float((s * xdp2) + b)).shortValue(); yoa2 = (new Float((s * ydp2) + d)).shortValue(); } int mapx(int x) { return (new Float((s * x) + b)).shortValue(); } int mapy(int y) { return (new Float((s * y) + d)).shortValue(); } //------------------------------------------------- // Here's an inner class that takes care of mouse presses. private class MyMouseListener implements MouseListener { public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); imageFocusX = (new Float((x - b)/s)).shortValue(); theView.imageFocusX = imageFocusX; imageFocusY = (new Float((y - d)/s)).shortValue(); theView.imageFocusY = imageFocusY; repaint(); } public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseClicked(MouseEvent e) {} } // Here's another inner class. This one takes care of mouse motion. private class MyMouseMotionListener implements MouseMotionListener { public void mouseMoved(MouseEvent e) { int x = e.getX(); int y = e.getY(); imageMouseX = (new Float((x - b)/s)).shortValue(); theView.imageMouseX = imageMouseX; imageMouseY = (new Float((y - d)/s)).shortValue(); theView.imageMouseY = imageMouseY; theView.pixelCoords.setText("(x, y) = (" + imageMouseX + ", " + (imageNROWS - imageMouseY - 1) + ")"); } public void mouseDragged(MouseEvent e) {} } } // ------------------------------------------------------------ class ViewImage extends Container implements AdjustmentListener { //a kind of Container Image im; ImageCanvas theCanvas; MediaTracker imTracker; int thePixels[]; ColorModel defaultRGBmodel; int windowWidth, windowHeight; int imageNCOLS, imageNROWS; int imageFocusX; // coords of where user is looking in the image. int imageFocusY; int imageMouseX, imageMouseY; //coords of mouse cursor. double s; // scale factor = width of an image pixel in the display window. int intScale; // integer version of s for use in drawing numbers at larger scales. double b, d; // translation parameters in image-to-window mapping. boolean showNumbers = true; boolean rgb; // true means show 3 color components. int currentScale; // = log base 2 of the ratio of screen pixel widths to image pixel widths. BorderLayout layout; Scrollbar vertbar; Scrollbar horizbar; Panel buttonBar; Button zoomInButton, zoomOutButton; Checkbox rgbBox; Label pixelCoords; public void init(Applet parent) { currentScale = 5; // for now, let's start with a blowup. rgb = true; theCanvas = new ImageCanvas(this); layout = new BorderLayout(); setLayout( layout ); add("Center", theCanvas); vertbar = new Scrollbar(Scrollbar.VERTICAL); horizbar = new Scrollbar(Scrollbar.HORIZONTAL); add("East", vertbar); add("South", horizbar); vertbar.addAdjustmentListener( this ); horizbar.addAdjustmentListener( this ); buttonBar = new Panel(); pixelCoords = new Label("(x, y) = ( , ) "); zoomInButton = new Button("Zoom in"); zoomInButton.addActionListener(new ZoomInListener()); zoomOutButton = new Button("Zoom out"); zoomOutButton.addActionListener(new ZoomOutListener()); rgbBox = new Checkbox("Show RGB values"); rgbBox.addItemListener(new MyCheckBoxListener()); rgbBox.setState(rgb); buttonBar.add(pixelCoords); buttonBar.add(zoomInButton); buttonBar.add(zoomOutButton); buttonBar.add(rgbBox); add("North", buttonBar); loadImage(parent); defaultRGBmodel = ColorModel.getRGBdefault(); } public void loadImage(Applet parent) { imTracker = new MediaTracker(this); try { if (JavaPixelCalculator.reallyAnApplet) { String imageFileName = parent.getParameter("IMAGE"); im = parent.getImage(parent.getCodeBase(), imageFileName); System.out.println("The image file is " + imageFileName); } else { im = Toolkit.getDefaultToolkit().getImage("mondo.jpg"); } } catch (Exception e) { System.out.println("Error in accessing image file"); } imTracker.addImage(im, 1); try { imTracker.waitForAll (); } catch (InterruptedException e) { return; } imageNCOLS = im.getWidth(this); imageNROWS = im.getHeight(this); imageFocusX = imageNCOLS / 2; imageFocusY = imageNROWS / 2; thePixels = new int[imageNCOLS * imageNROWS]; // Set up a PixelGrabber to copy the pixels of the image into // an ordinary array. PixelGrabber pixel_grabber = new PixelGrabber(im.getSource(), 0, 0, imageNCOLS, imageNROWS, thePixels, 0, imageNCOLS); try { pixel_grabber.grabPixels(); } catch (InterruptedException e) { System.out.println("Exception while grabbing pixels"); } invalidate(); } public void paint(Graphics g) { System.out.println("Entering ViewImage.paint"); theCanvas.setCanvasInfo(im, imageNCOLS, imageNROWS, imageFocusX, imageFocusY, thePixels, currentScale, rgb); theCanvas.paint(g); } public void update(Graphics g) { System.out.println("Entering ViewImage.update"); theCanvas.setCanvasInfo(im, imageNCOLS, imageNROWS, imageFocusX, imageFocusY, thePixels, currentScale, rgb); theCanvas.repaint(); } private int getPixel (int x, int y) { return (thePixels [y * imageNCOLS + x]); } private void putPixel (int x, int y, int rgb) { thePixels [y * imageNCOLS + x] = rgb; } public void adjustmentValueChanged(AdjustmentEvent e) { imageFocusY = vertbar.getValue(); imageFocusX = horizbar.getValue(); theCanvas.imageFocusX = imageFocusX; theCanvas.imageFocusY = imageFocusY; theCanvas.repaint(); } public synchronized void setBounds(int x, int y, int width, int height) { super.setBounds(x, y, width, height); int ymax = imageNROWS; vertbar.setValues(imageFocusY, ymax / 10, 0, ymax); int xmax = imageNCOLS; horizbar.setValues(imageFocusX, xmax / 10, 0, xmax); } private class ZoomInListener implements ActionListener { public void actionPerformed(ActionEvent e) { currentScale++; theCanvas.currentScale++; theCanvas.repaint(); } } private class ZoomOutListener implements ActionListener { public void actionPerformed(ActionEvent e) { currentScale--; theCanvas.currentScale--; theCanvas.repaint(); } } private class MyCheckBoxListener implements ItemListener { public void itemStateChanged(ItemEvent e) { if (rgb) rgb = false; else rgb = true; theCanvas.rgb = rgb; if (currentScale > 3) theCanvas.repaint(); } } } // end of ViewImage class // ------------------------------------------------------------------ public class JavaPixelCalculator extends Applet { public static boolean reallyAnApplet = true; public static void main( String [] args) { Frame f = new Frame(); reallyAnApplet = false; JavaPixelCalculator jpc = new JavaPixelCalculator(); f.add(jpc); jpc.init(); f.resize(600, 400); f.show(); jpc.invalidate(); jpc.start(); } ViewImage sp; public void init() { sp = new ViewImage(); setLayout(new BorderLayout()); add("Center", sp); sp.init(this); sp.theCanvas.repaint(); } public void paint(Graphics g) { sp.paint(g); } public void update(Graphics g) { sp.paint(g); } }