/****************************************** * CelebrityPainter: * * Applet lets users paint a blank face * * using facial features of celebrities. * * * * Created & Programmed by: * * Jeff Orkin * * * * Copyright 1996 * ******************************************///// To run this applet, you need to create an html page that// links in the applet, and provides parameter lines for celebrity // brushes to include.// The html to link in the applet should look like this:// // // // // //// You can add as many celebrities as you want. // To add celebrities:// 1. Add a parameter line to the applet tag in the html// that has the next sequential brush number, and // gives a name string.// 2. Provide a 160x200 jpeg of the celebrity, with a// filename that matches the name string without spaces.// 3. Provide a 40x50 jpeg of the celebrity, with a // filename that matches the name string without spaces// plus the characters "BR".//// For example, to add a David Letterman brush:// 1. Add the parameter line // // 2. Provide a 160x200 jpeg called DavidLetterman.jpg// 3. Provide a 40x50 jpeg called DavidLettermanBR.jpg// // I M P O R T S ///////////////////////////////////////////////////////import java.applet.Applet;import java.awt.*;import java.awt.image.*;// C L A S S E S ///////////////////////////////////////////////////////// class CelebrityPainter:// Main applet class for the applet//public class CelebrityPainter extends Applet { // CONSTANTS ////////////////////////////////////////////////////// static final int SMALL = 1; static final int MEDIUM = 2; static final int LARGE = 3; // MEMBERS ///////////////////////////////////////////// ControlPanel cpControls; // Applet's control panel Image drawnPic; // Double buffer to draw on Graphics drawnPicGC; Graphics screenGC; Brush bshCeleb; // Brush to paint with // // init: // Set up the applet by setting the background to white and creating // the control panel, brush, and double buffer. // public void init() { this.setBackground(Color.white); setLayout(new BorderLayout()); cpControls = new ControlPanel(this); add("East", cpControls); // Set brush to the first Celebrity in the combo box of names. bshCeleb = new Brush(cpControls.sSelected, this); // Create a graphics context for the screen so that // functions other than paint() and update() can draw // to the screen. screenGC = this.getGraphics(); // Create a double buffer same size as applet to draw on. drawnPic = createImage(size().width, size().height); drawnPicGC = drawnPic.getGraphics(); // Paint the blank face background. clear(); } // // clear: // Paint a blank face over whatever's in the buffer, // and repaint the applet. // public void clear() { // Draw blue background. drawnPicGC.setColor(Color.blue); drawnPicGC.fillRect(0, 0, 160, 200); // Draw pink oval as blank face. drawnPicGC.setColor(Color.pink); drawnPicGC.fillOval(26, 12, 108, 176); // Copy double buffer to the screen. repaint(); } // // paint: // Copy the double buffer to the screen. // public void paint(Graphics g) { g.drawImage(drawnPic, 0, 0, this); } // // mouseDrag: // This is where most of the action takes place. // Copy the pixels surrounding the mouse from the current // celebrity picture, held in the Brush, to the double buffer. // Finally, copy the modified double buffer to the screen. // The number of pixels affected depends on the current // brush size held in the control panel. // public boolean mouseDrag(Event e, int x, int y) { int i, j; Color pixColor; // Ignore mouse draging in the control panel. if(x >= 160) return true; // Number of pixels affected depends on the brush size. switch (cpControls.iBrushSize) { // small brush case SMALL: // Copy pixels surrounding mouse from the celebrity brush // to the double buffer. for(i=x-1; i<=x+1; i++) { for(j=y-3; j<=y+3; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } } i=x-3; for(j=y-1; j<=y+1; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x-2; for(j=y-2; j<=y+2; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x+3; for(j=y-1; j<=y+1; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x+2; for(j=y-2; j<=y+2; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } break; // medium brush case MEDIUM: // Copy pixels surrounding mouse from the celebrity brush // to the double buffer. for(i=x-2; i<=x+2; i++) { for(j=y-4; j<=y+4; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } } i=x-4; for(j=y-2; j<=y+2; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x-3; for(j=y-3; j<=y+3; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x+4; for(j=y-2; j<=y+2; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x+3; for(j=y-3; j<=y+3; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } break; // large brush case LARGE: // Copy pixels surrounding mouse from the celebrity brush // to the double buffer. for(i=x-3; i<=x+3; i++) { for(j=y-5; j<=y+5; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } } i=x-5; for(j=y-3; j<=y+3; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x-4; for(j=y-4; j<=y+4; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x+5; for(j=y-3; j<=y+3; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } i=x+4; for(j=y-4; j<=y+4; j++) { drawnPicGC.setColor(new Color(bshCeleb.iImgPixels[i][j])); drawnPicGC.drawLine(i, j, i, j); } break; } // end of switch // Copy double buffer to the screen. // Done locally rather than calling repaint() for speed. screenGC.drawImage(drawnPic, 0, 0, this); return true; }} // end of CelebrityPainter//// class ControlPanel:// Class responsible for creating and managing// the applets GUI.//class ControlPanel extends Panel{ // CONSTANTS ////////////////////////////////////////////////////// static final int SMALL = 1; static final int MEDIUM = 2; static final int LARGE = 3; // MEMBERS ///////////////////////////////////////////// BrushCanvas bcBrushPic; // canvas to paint thumbnail image of brush on Checkbox optSmall; Checkbox optMedium; Checkbox optLarge; Checkbox optEraser; int iBrushSize; // currently selected brush size String sSelected; // currently selected celebrity brush CelebrityPainter appParent; // // ControlPanel: // Constructor for the Control Panel. // Creates and places components of the GUI. // public ControlPanel(CelebrityPainter ap) { Choice cboBrush; Button btnClear; CheckboxGroup grpBrushSize; Label lblBrushSize; GridBagLayout gblControls = new GridBagLayout(); GridBagConstraints gbcControls = new GridBagConstraints(); appParent = ap; setFont(new Font("Helvetica", Font.PLAIN, 14)); setLayout(gblControls); // Set spacing for controls. gbcControls.insets = new Insets(0, 3, 0, 0); // cboBrush /////////////////////////////////////////////////////////////////// // cboBrush is the last control on row of grid. gbcControls.gridwidth = GridBagConstraints.REMAINDER; cboBrush = new Choice(); gblControls.setConstraints(cboBrush, gbcControls); // Fill combo box with names given as parameters to the applet in the html. fillCombo(cboBrush); // Set selected to start out holding the filename of the jpg of the first // celebrity listed in the combo box. sSelected = convertImageString(cboBrush.getItem(0), ""); add(cboBrush); // bcBrushPic /////////////////////////////////////////////////////////////////// // Set grid constraints to allow the bush pic and clear button to share a row. gbcControls.weightx = 1.0; gbcControls.gridwidth = 1; // Create the canvas initially showing the thumbnail of the first celebrity in the list. bcBrushPic = new BrushCanvas(convertImageString(cboBrush.getItem(0), "BR"), appParent); bcBrushPic.resize(40, 50); gblControls.setConstraints(bcBrushPic, gbcControls); add(bcBrushPic); // btnClear /////////////////////////////////////////////////////////////////// // Set grid constraints to make btnClear the last control on the row, // and weight it so that it takes up less than half of the row. gbcControls.gridwidth = GridBagConstraints.REMAINDER; gbcControls.fill = GridBagConstraints.VERTICAL; gbcControls.weightx = 0.4; btnClear = new Button("CLEAR"); gblControls.setConstraints(btnClear, gbcControls); add(btnClear); // Eraser Option Button /////////////////////////////////////////////////////////////////// // Set grid constraints that let each control from here on takes up a // whole line, which makes them left align. gbcControls.fill = GridBagConstraints.BOTH; optEraser = new Checkbox("Eraser"); gblControls.setConstraints(optEraser, gbcControls); add(optEraser); // Brush Size Option Buttons /////////////////////////////////////////////////////////////////// lblBrushSize = new Label("Brush Size:"); gblControls.setConstraints(lblBrushSize, gbcControls); add(lblBrushSize); grpBrushSize = new CheckboxGroup(); // Set initial brush size to small. iBrushSize = SMALL; optSmall = new Checkbox("Small", grpBrushSize, true); gblControls.setConstraints(optSmall, gbcControls); add(optSmall); optMedium = new Checkbox("Medium", grpBrushSize, false); gblControls.setConstraints(optMedium, gbcControls); add(optMedium); optLarge = new Checkbox("Large", grpBrushSize, false); gblControls.setConstraints(optLarge, gbcControls); add(optLarge); } // // fillCombo: // Fills the combo box of celebrity brushes with names // given as parameters through the html. // The number of brushes is unlimited. // public void fillCombo(Choice cboList) { String sListItem; // String to add as a combo choice int iNum; // Current parameter to get // Look first for parameter brush1. iNum = 1; while(true) { sListItem = appParent.getParameter("brush" + iNum); // Stop looping when there are no more brush parameters. if(sListItem == null) break; cboList.addItem(sListItem); // Look for the next brush parameter incrementally. iNum++; } } // // action: // Act on the afftected control panel component. // public boolean action(Event e, Object arg) { int iSelectedLen; // Length of selected brush name in combo box // Used for erasing. // Process celebrity name combo box event. if(e.target instanceof Choice) { // Tell user that the brush is being loaded. appParent.screenGC.setColor(Color.white); appParent.screenGC.fillRect(0, 180, 160, 200); appParent.screenGC.setColor(Color.black); appParent.screenGC.drawString("Loading Brush.....", 20, 195); // Turn off the eraser, in case it was on. optEraser.setState(false); // Set the thumbnail celebrity image to one matching the selected name. bcBrushPic.setImage(convertImageString((String)arg, "BR")); sSelected = convertImageString((String)arg, ""); bcBrushPic.repaint(); // Load the selected name's celebrity image into the applet's brush. appParent.bshCeleb.setImage(sSelected); // Erase the loading message by redrawing the applet's screen. appParent.screenGC.drawImage(appParent.drawnPic, 0, 0, appParent); } // Process option event. if(e.target instanceof Checkbox) { // Process eraser event if(e.target == optEraser) { // Load the eraser if(optEraser.getState() == true) { // Tell user that the eraser is being loaded. appParent.screenGC.setColor(Color.white); appParent.screenGC.fillRect(0, 180, 160, 200); appParent.screenGC.setColor(Color.black); appParent.screenGC.drawString("Loading Eraser.....", 20, 195); // Set the thumbnail to image of background. bcBrushPic.setImage("ERASERBR.jpg"); bcBrushPic.repaint(); // Load the eraser image into the applet's brush. appParent.bshCeleb.setImage("ERASER.jpg"); // Erase the loading message by redrawing the applet's screen. appParent.screenGC.drawImage(appParent.drawnPic, 0, 0, appParent); } // Reload last selected brush. else { // Tell user that the brush is being loaded. appParent.screenGC.setColor(Color.white); appParent.screenGC.fillRect(0, 180, 160, 200); appParent.screenGC.setColor(Color.black); appParent.screenGC.drawString("Loading Brush.....", 20, 195); // Get length of string holding name of selected celeb's image file. iSelectedLen = sSelected.length(); // Set the thumbnail celebrity image to one matching the selected name. bcBrushPic.setImage(convertImageString(sSelected.substring(0, iSelectedLen - 4), "BR")); bcBrushPic.repaint(); // Load the selected name's celebrity image into the applet's brush. appParent.bshCeleb.setImage(sSelected); // Erase the loading message by redrawing the applet's screen. appParent.screenGC.drawImage(appParent.drawnPic, 0, 0, appParent); } } // Process brush size event else { // Set the brush size to correspond to the option button set to true. if(optSmall.getState() == true) iBrushSize = SMALL; else if(optMedium.getState() == true) iBrushSize = MEDIUM; else if(optLarge.getState() == true) iBrushSize = LARGE; } } // Process clear button. if(e.target instanceof Button) { // Clear the applet's screen, setting it back to a blank face. appParent.clear(); } return true; } // // convertImageString: // Convert a celebrity name from the combo box into the name // of a jpg file to be loaded. Do this by stripping spaces, adding // any extra characters, and adding the ".jpg" extension. // public String convertImageString(String sImageString, String sExt) { String sImageName = null; // String to hold the final image name int iSpace = 0; // Location of current space int iLastSpace = (-1); // Location of last space int iNameLength = 0; // Length of given string iNameLength = sImageString.length(); // Keep stripping spaces until there are no more. while(iSpace != (-1)) { iSpace = sImageString.indexOf(" ", iLastSpace + 1); // If a space was found, add the next string segment to the file name. if(iSpace != (-1)) { if(sImageName == null) sImageName = sImageString.substring(iLastSpace + 1, iSpace); else sImageName += sImageString.substring(iLastSpace + 1, iSpace); iLastSpace = iSpace; } } // end of while // Add the string segment found after the last space (or no space). if(sImageName == null) sImageName = sImageString.substring(iLastSpace + 1, iNameLength) + sExt + ".jpg"; else sImageName += sImageString.substring(iLastSpace + 1, iNameLength) + sExt + ".jpg"; return sImageName; } } // end of ControlPanel//// class BrushCanvas:// Canvas to paint thumbnail of currently selected celebrity on.//class BrushCanvas extends Canvas{ // MEMBERS ///////////////////////////////////////////// Applet appParent; Brush bshCelebPic; // Brush used to hold current celebrity thumbnail Image imgCanvasImage; Graphics canvasImageGC; public BrushCanvas(String sImageName, Applet ap) { appParent = ap; // Create the canvas' image. imgCanvasImage = appParent.createImage(40, 50); canvasImageGC = imgCanvasImage.getGraphics(); // Create a new brush from the given image. bshCelebPic = new Brush(sImageName, appParent); // Call set image to copy the image to the canvas. setImage(sImageName); } // // setImage: // Copies specified image to canvas by setting it in // the canvas' brush, and copying the pixles. // This process is slow, but produces a better quality image // than simply using getImage() and drawImage(). // public void setImage(String sImageName) { // Set the specified image in the canvas' brush. bshCelebPic.setImage(sImageName); // Copy pixels of the canvas' brush to the canvas. for(int x=0; x<40; x++) { for(int y=0; y<50; y++) { canvasImageGC.setColor(new Color(bshCelebPic.iImgPixels[x][y])); canvasImageGC.drawLine(x, y, x, y); } } } // // update: // Call paint without clearing first. // public void update(Graphics g) { paint(g); } // // paint: // Copy the canvas to the screen. // public void paint(Graphics g) { g.drawImage(imgCanvasImage, 0, 0, appParent); }} // end of BrushCanvas//// class Brush:// Creates an array of pixel values from a given Image.//class Brush implements ImageConsumer { // MEMBERS ///////////////////////////////////////////// Applet appParent; boolean bImgReady; // Flag set when image has been consumed int iImgXSize, iImgYSize; int iImgPixels[][]; // Actual pizel values of image // // Brush: // Constructor calls setImage to extract pixels frmo the given image. // Brush(String sImageName, Applet ap) { appParent = ap; // Extract pixels from given image. setImage(sImageName); } // // setImage: // Extracts pixels from a given image. // public void setImage(String sImageName) { int iTicks; Image imgCopy; imgCopy = appParent.getImage(appParent.getDocumentBase(), sImageName); bImgReady = false; // Get pixles, but give up after 5 minutes. imgCopy.getSource().startProduction(this); iTicks = 5*60*1000; // 5 minutes while(iTicks>0) { try { Thread.currentThread().sleep(100); } catch (Exception e) {} if(this.bImgReady) break; iTicks -= 100; } } // Function required for Image Consumer. public void setProperties(java.util.Hashtable dummy) { } // Function required for Image Consumer. public void setColorModel(ColorModel model) { } // Function required for Image Consumer. public void setHints(int dummy) { } // // imageComplete: // Notifies brush that an image has been consumed by // setting the image ready flag to true. // public void imageComplete(int dummy) { bImgReady = true; } // // setDimensions: // Sets variables for dimensions of given image, // and creates an array to hold the pixels. // public void setDimensions(int x, int y) { iImgXSize = x; iImgYSize = y; iImgPixels = new int[x][y]; } // // setPixels: // Take pixels from the one dimensional array and put their // RGB lookup values into the two dimensional array of pixels. // public void setPixels(int x1, int y1, int w, int h, ColorModel cmModel, byte byPixels[], int iOff, int iScansize) { int x, y, x2, y2, sx, sy; x1 = x2 = iOff = 0; x2 = x1+w; y2 = y1+h; sy = iOff; for(y=y1; y