Mega Code Archive

 
Categories / Java / SWT Jface Eclipse
 

Image Analyzer in SWT

import java.io.InputStream; import java.net.URL; import java.text.MessageFormat; import java.util.ResourceBundle; import java.util.Vector; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTError; import org.eclipse.swt.SWTException; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.ShellAdapter; import org.eclipse.swt.events.ShellEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageLoader; import org.eclipse.swt.graphics.ImageLoaderEvent; import org.eclipse.swt.graphics.ImageLoaderListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.printing.PrintDialog; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.printing.PrinterData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Dialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Sash; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; public class ImageAnalyzer {   Display display;   Shell shell;   Canvas imageCanvas, paletteCanvas;   Label typeLabel, sizeLabel, depthLabel, transparentPixelLabel,       timeToLoadLabel, screenSizeLabel, backgroundPixelLabel,       locationLabel, disposalMethodLabel, delayTimeLabel,       repeatCountLabel, paletteLabel, dataLabel, statusLabel;   Combo backgroundCombo, scaleXCombo, scaleYCombo, alphaCombo;   Button incrementalCheck, transparentCheck, maskCheck, backgroundCheck;   Button previousButton, nextButton, animateButton;   StyledText dataText;   Sash sash;   Color whiteColor, blackColor, redColor, greenColor, blueColor,       canvasBackground;   Font fixedWidthFont;   Cursor crossCursor;   GC imageCanvasGC;   int paletteWidth = 140; // recalculated and used as a width hint   int ix = 0, iy = 0, py = 0; // used to scroll the image and palette   float xscale = 1, yscale = 1; // used to scale the image   int alpha = 255; // used to modify the alpha value of the image   boolean incremental = false; // used to incrementally display an image   boolean transparent = true; // used to display an image with transparency   boolean showMask = false; // used to display an icon mask or transparent                 // image mask   boolean showBackground = false; // used to display the background of an                   // animated image   boolean animate = false; // used to animate a multi-image file   Thread animateThread; // draws animated images   Thread incrementalThread; // draws incremental images   String lastPath; // used to seed the file dialog   String currentName; // the current image file or URL name   String fileName; // the current image file   ImageLoader loader; // the loader for the current image file   ImageData[] imageDataArray; // all image data read from the current file   int imageDataIndex; // the index of the current image data   ImageData imageData; // the currently-displayed image data   Image image; // the currently-displayed image   Vector incrementalEvents; // incremental image events   long loadTime = 0; // the time it took to load the current image   static final int INDEX_DIGITS = 4;   static final int ALPHA_CONSTANT = 0;   static final int ALPHA_X = 1;   static final int ALPHA_Y = 2;   class TextPrompter extends Dialog {     String message = "";     String result = null;     Shell dialog;     Text text;     public TextPrompter(Shell parent, int style) {       super(parent, style);     }     public TextPrompter(Shell parent) {       this(parent, SWT.APPLICATION_MODAL);     }     public String getMessage() {       return message;     }     public void setMessage(String string) {       message = string;     }     public String open() {       dialog = new Shell(getParent(), getStyle());       dialog.setText(getText());       dialog.setLayout(new GridLayout());       Label label = new Label(dialog, SWT.NULL);       label.setText(message);       label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));       text = new Text(dialog, SWT.SINGLE | SWT.BORDER);       GridData data = new GridData(GridData.FILL_HORIZONTAL);       data.widthHint = 300;       text.setLayoutData(data);       Composite buttons = new Composite(dialog, SWT.NONE);       GridLayout grid = new GridLayout();       grid.numColumns = 2;       buttons.setLayout(grid);       buttons.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));       Button ok = new Button(buttons, SWT.PUSH);       ok.setText("OK");       data = new GridData();       data.widthHint = 75;       ok.setLayoutData(data);       ok.addSelectionListener(new SelectionAdapter() {         public void widgetSelected(SelectionEvent e) {           result = text.getText();           dialog.dispose();         }       });       Button cancel = new Button(buttons, SWT.PUSH);       cancel.setText("Cancel");       data = new GridData();       data.widthHint = 75;       cancel.setLayoutData(data);       cancel.addSelectionListener(new SelectionAdapter() {         public void widgetSelected(SelectionEvent e) {           dialog.dispose();         }       });       dialog.setDefaultButton(ok);       dialog.pack();       dialog.open();       while (!dialog.isDisposed()) {         if (!display.readAndDispatch())           display.sleep();       }       return result;     }   }   public static void main(String[] args) {     Display display = new Display();     ImageAnalyzer imageAnalyzer = new ImageAnalyzer();     Shell shell = imageAnalyzer.open(display);     while (!shell.isDisposed())       if (!display.readAndDispatch())         display.sleep();     display.dispose();   }   public Shell open(Display dpy) {     // Create a window and set its title.     this.display = dpy;     shell = new Shell(display);     shell.setText("Image_analyzer");     // Hook resize and dispose listeners.     shell.addControlListener(new ControlAdapter() {       public void controlResized(ControlEvent event) {         resizeShell(event);       }     });     shell.addShellListener(new ShellAdapter() {       public void shellClosed(ShellEvent e) {         animate = false; // stop any animation in progress         if (animateThread != null) {           // wait for the thread to die before disposing the shell.           while (animateThread.isAlive()) {             if (!display.readAndDispatch())               display.sleep();           }         }         e.doit = true;       }     });     shell.addDisposeListener(new DisposeListener() {       public void widgetDisposed(DisposeEvent e) {         // Clean up.         if (image != null)           image.dispose();         whiteColor.dispose();         blackColor.dispose();         redColor.dispose();         greenColor.dispose();         blueColor.dispose();         fixedWidthFont.dispose();         crossCursor.dispose();       }     });     // Create colors and fonts.     whiteColor = new Color(display, 255, 255, 255);     blackColor = new Color(display, 0, 0, 0);     redColor = new Color(display, 255, 0, 0);     greenColor = new Color(display, 0, 255, 0);     blueColor = new Color(display, 0, 0, 255);     fixedWidthFont = new Font(display, "courier", 10, 0);     crossCursor = new Cursor(display, SWT.CURSOR_CROSS);     // Add a menu bar and widgets.     createMenuBar();     createWidgets();     shell.pack();     // Create a GC for drawing, and hook the listener to dispose it.     imageCanvasGC = new GC(imageCanvas);     imageCanvas.addDisposeListener(new DisposeListener() {       public void widgetDisposed(DisposeEvent e) {         imageCanvasGC.dispose();       }     });     // Open the window     shell.open();     return shell;   }   void createWidgets() {     // Add the widgets to the shell in a grid layout.     GridLayout layout = new GridLayout();     layout.marginHeight = 0;     layout.numColumns = 2;     shell.setLayout(layout);     // Separate the menu bar from the rest of the widgets.     Label separator = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);     GridData gridData = new GridData();     gridData.horizontalSpan = 2;     gridData.horizontalAlignment = GridData.FILL;     separator.setLayoutData(gridData);     // Add a composite to contain some control widgets across the top.     Composite controls = new Composite(shell, SWT.NULL);     RowLayout rowLayout = new RowLayout();     rowLayout.marginTop = 0;     rowLayout.marginBottom = 5;     rowLayout.spacing = 8;     controls.setLayout(rowLayout);     gridData = new GridData();     gridData.horizontalSpan = 2;     controls.setLayoutData(gridData);     // Combo to change the background.     Group group = new Group(controls, SWT.NULL);     group.setLayout(new RowLayout());     group.setText("Background");     backgroundCombo = new Combo(group, SWT.DROP_DOWN | SWT.READ_ONLY);     backgroundCombo.setItems(new String[] { "None",         "White", "Black",         "Red", "Green",         "Blue" });     backgroundCombo.select(backgroundCombo.indexOf("White"));     backgroundCombo.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         changeBackground();       }     });     // Combo to change the x scale.     String[] values = { "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7",         "0.8", "0.9", "1", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6",         "1.7", "1.8", "1.9", "2", "3", "4", "5", "6", "7", "8", "9",         "10", };     group = new Group(controls, SWT.NULL);     group.setLayout(new RowLayout());     group.setText("X_scale");     scaleXCombo = new Combo(group, SWT.DROP_DOWN);     for (int i = 0; i < values.length; i++) {       scaleXCombo.add(values[i]);     }     scaleXCombo.select(scaleXCombo.indexOf("1"));     scaleXCombo.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         scaleX();       }     });     // Combo to change the y scale.     group = new Group(controls, SWT.NULL);     group.setLayout(new RowLayout());     group.setText("Y_scale");     scaleYCombo = new Combo(group, SWT.DROP_DOWN);     for (int i = 0; i < values.length; i++) {       scaleYCombo.add(values[i]);     }     scaleYCombo.select(scaleYCombo.indexOf("1"));     scaleYCombo.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         scaleY();       }     });     // Combo to change the alpha value.     group = new Group(controls, SWT.NULL);     group.setLayout(new RowLayout());     group.setText("Alpha_K");     alphaCombo = new Combo(group, SWT.DROP_DOWN | SWT.READ_ONLY);     for (int i = 0; i <= 255; i += 5) {       alphaCombo.add(String.valueOf(i));     }     alphaCombo.select(alphaCombo.indexOf("255"));     alphaCombo.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         alpha();       }     });     // Check box to request incremental display.     group = new Group(controls, SWT.NULL);     group.setLayout(new RowLayout());     group.setText("Display");     incrementalCheck = new Button(group, SWT.CHECK);     incrementalCheck.setText("Incremental");     incrementalCheck.setSelection(incremental);     incrementalCheck.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         incremental = ((Button) event.widget).getSelection();       }     });     // Check box to request transparent display.     transparentCheck = new Button(group, SWT.CHECK);     transparentCheck.setText("Transparent");     transparentCheck.setSelection(transparent);     transparentCheck.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         transparent = ((Button) event.widget).getSelection();         if (image != null) {           imageCanvas.redraw();         }       }     });     // Check box to request mask display.     maskCheck = new Button(group, SWT.CHECK);     maskCheck.setText("Mask");     maskCheck.setSelection(showMask);     maskCheck.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         showMask = ((Button) event.widget).getSelection();         if (image != null) {           imageCanvas.redraw();         }       }     });     // Check box to request background display.     backgroundCheck = new Button(group, SWT.CHECK);     backgroundCheck.setText("Background");     backgroundCheck.setSelection(showBackground);     backgroundCheck.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         showBackground = ((Button) event.widget).getSelection();       }     });     // Group the animation buttons.     group = new Group(controls, SWT.NULL);     group.setLayout(new RowLayout());     group.setText("Animation");     // Push button to display the previous image in a multi-image file.     previousButton = new Button(group, SWT.PUSH);     previousButton.setText("Previous");     previousButton.setEnabled(false);     previousButton.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         previous();       }     });     // Push button to display the next image in a multi-image file.     nextButton = new Button(group, SWT.PUSH);     nextButton.setText("Next");     nextButton.setEnabled(false);     nextButton.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         next();       }     });     // Push button to toggle animation of a multi-image file.     animateButton = new Button(group, SWT.PUSH);     animateButton.setText("Animate");     animateButton.setEnabled(false);     animateButton.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         animate();       }     });     // Label to show the image file type.     typeLabel = new Label(shell, SWT.NULL);     typeLabel.setText("Type_initial");     typeLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));     // Canvas to show the image.     imageCanvas = new Canvas(shell, SWT.V_SCROLL | SWT.H_SCROLL         | SWT.NO_REDRAW_RESIZE);     imageCanvas.setBackground(whiteColor);     imageCanvas.setCursor(crossCursor);     gridData = new GridData();     gridData.verticalSpan = 15;     gridData.horizontalAlignment = GridData.FILL;     gridData.verticalAlignment = GridData.FILL;     gridData.grabExcessHorizontalSpace = true;     gridData.grabExcessVerticalSpace = true;     imageCanvas.setLayoutData(gridData);     imageCanvas.addPaintListener(new PaintListener() {       public void paintControl(PaintEvent event) {         if (image != null)           paintImage(event);       }     });     imageCanvas.addMouseMoveListener(new MouseMoveListener() {       public void mouseMove(MouseEvent event) {         if (image != null) {           showColorAt(event.x, event.y);         }       }     });     // Set up the image canvas scroll bars.     ScrollBar horizontal = imageCanvas.getHorizontalBar();     horizontal.setVisible(true);     horizontal.setMinimum(0);     horizontal.setEnabled(false);     horizontal.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         scrollHorizontally((ScrollBar) event.widget);       }     });     ScrollBar vertical = imageCanvas.getVerticalBar();     vertical.setVisible(true);     vertical.setMinimum(0);     vertical.setEnabled(false);     vertical.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         scrollVertically((ScrollBar) event.widget);       }     });     // Label to show the image size.     sizeLabel = new Label(shell, SWT.NULL);     sizeLabel.setText("Size_initial");     sizeLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the image depth.     depthLabel = new Label(shell, SWT.NULL);     depthLabel.setText("Depth_initial");     depthLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the transparent pixel.     transparentPixelLabel = new Label(shell, SWT.NULL);     transparentPixelLabel.setText("Transparent_pixel_initial");     transparentPixelLabel.setLayoutData(new GridData(         GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the time to load.     timeToLoadLabel = new Label(shell, SWT.NULL);     timeToLoadLabel.setText("Time_to_load_initial");     timeToLoadLabel.setLayoutData(new GridData(         GridData.HORIZONTAL_ALIGN_FILL));     // Separate the animation fields from the rest of the fields.     separator = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);     separator.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the logical screen size for animation.     screenSizeLabel = new Label(shell, SWT.NULL);     screenSizeLabel.setText("Animation_size_initial");     screenSizeLabel.setLayoutData(new GridData(         GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the background pixel.     backgroundPixelLabel = new Label(shell, SWT.NULL);     backgroundPixelLabel.setText("Background_pixel_initial");     backgroundPixelLabel.setLayoutData(new GridData(         GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the image location (x, y).     locationLabel = new Label(shell, SWT.NULL);     locationLabel.setText("Image_location_initial");     locationLabel         .setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the image disposal method.     disposalMethodLabel = new Label(shell, SWT.NULL);     disposalMethodLabel.setText("Disposal_initial");     disposalMethodLabel.setLayoutData(new GridData(         GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the image delay time.     delayTimeLabel = new Label(shell, SWT.NULL);     delayTimeLabel.setText("Delay_initial");     delayTimeLabel.setLayoutData(new GridData(         GridData.HORIZONTAL_ALIGN_FILL));     // Label to show the background pixel.     repeatCountLabel = new Label(shell, SWT.NULL);     repeatCountLabel.setText("Repeats_initial");     repeatCountLabel.setLayoutData(new GridData(         GridData.HORIZONTAL_ALIGN_FILL));     // Separate the animation fields from the palette.     separator = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);     separator.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));     // Label to show if the image has a direct or indexed palette.     paletteLabel = new Label(shell, SWT.NULL);     paletteLabel.setText("Palette_initial");     paletteLabel         .setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));     // Canvas to show the image's palette.     paletteCanvas = new Canvas(shell, SWT.BORDER | SWT.V_SCROLL         | SWT.NO_REDRAW_RESIZE);     paletteCanvas.setFont(fixedWidthFont);     paletteCanvas.getVerticalBar().setVisible(true);     gridData = new GridData();     gridData.horizontalAlignment = GridData.FILL;     gridData.verticalAlignment = GridData.FILL;     GC gc = new GC(paletteLabel);     paletteWidth = gc.stringExtent("Max_length_string").x;     gc.dispose();     gridData.widthHint = paletteWidth;     gridData.heightHint = 16 * 11; // show at least 16 colors     paletteCanvas.setLayoutData(gridData);     paletteCanvas.addPaintListener(new PaintListener() {       public void paintControl(PaintEvent event) {         if (image != null)           paintPalette(event);       }     });     // Set up the palette canvas scroll bar.     vertical = paletteCanvas.getVerticalBar();     vertical.setVisible(true);     vertical.setMinimum(0);     vertical.setIncrement(10);     vertical.setEnabled(false);     vertical.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         scrollPalette((ScrollBar) event.widget);       }     });     // Sash to see more of image or image data.     sash = new Sash(shell, SWT.HORIZONTAL);     gridData = new GridData();     gridData.horizontalSpan = 2;     gridData.horizontalAlignment = GridData.FILL;     sash.setLayoutData(gridData);     sash.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         if (event.detail != SWT.DRAG) {           ((GridData) paletteCanvas.getLayoutData()).heightHint = SWT.DEFAULT;           Rectangle paletteCanvasBounds = paletteCanvas.getBounds();           int minY = paletteCanvasBounds.y + 20;           Rectangle dataLabelBounds = dataLabel.getBounds();           int maxY = statusLabel.getBounds().y               - dataLabelBounds.height - 20;           if (event.y > minY && event.y < maxY) {             Rectangle oldSash = sash.getBounds();             sash.setBounds(event.x, event.y, event.width,                 event.height);             int diff = event.y - oldSash.y;             Rectangle bounds = imageCanvas.getBounds();             imageCanvas.setBounds(bounds.x, bounds.y, bounds.width,                 bounds.height + diff);             bounds = paletteCanvasBounds;             paletteCanvas.setBounds(bounds.x, bounds.y,                 bounds.width, bounds.height + diff);             bounds = dataLabelBounds;             dataLabel.setBounds(bounds.x, bounds.y + diff,                 bounds.width, bounds.height);             bounds = dataText.getBounds();             dataText.setBounds(bounds.x, bounds.y + diff,                 bounds.width, bounds.height - diff);             // shell.layout(true);           }         }       }     });     // Label to show data-specific fields.     dataLabel = new Label(shell, SWT.NULL);     dataLabel.setText("Pixel_data_initial");     gridData = new GridData();     gridData.horizontalSpan = 2;     gridData.horizontalAlignment = GridData.FILL;     dataLabel.setLayoutData(gridData);     // Text to show a dump of the data.     dataText = new StyledText(shell, SWT.BORDER | SWT.MULTI | SWT.READ_ONLY         | SWT.V_SCROLL | SWT.H_SCROLL);     dataText.setBackground(display         .getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));     dataText.setFont(fixedWidthFont);     gridData = new GridData();     gridData.horizontalSpan = 2;     gridData.horizontalAlignment = GridData.FILL;     gridData.verticalAlignment = GridData.FILL;     gridData.heightHint = 128;     gridData.grabExcessVerticalSpace = true;     dataText.setLayoutData(gridData);     dataText.addMouseListener(new MouseAdapter() {       public void mouseDown(MouseEvent event) {         if (image != null && event.button == 1) {           showColorForData();         }       }     });     dataText.addKeyListener(new KeyAdapter() {       public void keyPressed(KeyEvent event) {         if (image != null) {           showColorForData();         }       }     });     // Label to show status and cursor location in image.     statusLabel = new Label(shell, SWT.NULL);     statusLabel.setText("");     gridData = new GridData();     gridData.horizontalSpan = 2;     gridData.horizontalAlignment = GridData.FILL;     statusLabel.setLayoutData(gridData);   }   Menu createMenuBar() {     // Menu bar.     Menu menuBar = new Menu(shell, SWT.BAR);     shell.setMenuBar(menuBar);     createFileMenu(menuBar);     createAlphaMenu(menuBar);     return menuBar;   }   void createFileMenu(Menu menuBar) {     // File menu     MenuItem item = new MenuItem(menuBar, SWT.CASCADE);     item.setText("File");     Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);     item.setMenu(fileMenu);     // File -> Open File...     item = new MenuItem(fileMenu, SWT.PUSH);     item.setText("OpenFile");     item.setAccelerator(SWT.MOD1 + 'O');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuOpenFile();       }     });     // File -> Open URL...     item = new MenuItem(fileMenu, SWT.PUSH);     item.setText("OpenURL");     item.setAccelerator(SWT.MOD1 + 'U');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuOpenURL();       }     });     // File -> Reopen     item = new MenuItem(fileMenu, SWT.PUSH);     item.setText("Reopen");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuReopen();       }     });     new MenuItem(fileMenu, SWT.SEPARATOR);     // File -> Save     item = new MenuItem(fileMenu, SWT.PUSH);     item.setText("Save");     item.setAccelerator(SWT.MOD1 + 'S');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuSave();       }     });     // File -> Save As...     item = new MenuItem(fileMenu, SWT.PUSH);     item.setText("Save_as");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuSaveAs();       }     });     // File -> Save Mask As...     item = new MenuItem(fileMenu, SWT.PUSH);     item.setText("Save_mask_as");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuSaveMaskAs();       }     });     new MenuItem(fileMenu, SWT.SEPARATOR);     // File -> Print     item = new MenuItem(fileMenu, SWT.PUSH);     item.setText("Print");     item.setAccelerator(SWT.MOD1 + 'P');     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuPrint();       }     });     new MenuItem(fileMenu, SWT.SEPARATOR);     // File -> Exit     item = new MenuItem(fileMenu, SWT.PUSH);     item.setText("Exit");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         shell.close();       }     });   }   void createAlphaMenu(Menu menuBar) {     // Alpha menu     MenuItem item = new MenuItem(menuBar, SWT.CASCADE);     item.setText("Alpha");     Menu alphaMenu = new Menu(shell, SWT.DROP_DOWN);     item.setMenu(alphaMenu);     // Alpha -> K     item = new MenuItem(alphaMenu, SWT.PUSH);     item.setText("K");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuComposeAlpha(ALPHA_CONSTANT);       }     });     // Alpha -> (K + x) % 256     item = new MenuItem(alphaMenu, SWT.PUSH);     item.setText("(K + x) % 256");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuComposeAlpha(ALPHA_X);       }     });     // Alpha -> (K + y) % 256     item = new MenuItem(alphaMenu, SWT.PUSH);     item.setText("(K + y) % 256");     item.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         menuComposeAlpha(ALPHA_Y);       }     });   }   void menuComposeAlpha(int alpha_op) {     if (image == null)       return;     animate = false; // stop any animation in progress     Cursor waitCursor = new Cursor(display, SWT.CURSOR_WAIT);     shell.setCursor(waitCursor);     imageCanvas.setCursor(waitCursor);     try {       if (alpha_op == ALPHA_CONSTANT) {         imageData.alpha = alpha;       } else {         imageData.alpha = -1;         switch (alpha_op) {         case ALPHA_X:           for (int y = 0; y < imageData.height; y++) {             for (int x = 0; x < imageData.width; x++) {               imageData.setAlpha(x, y, (x + alpha) % 256);             }           }           break;         case ALPHA_Y:           for (int y = 0; y < imageData.height; y++) {             for (int x = 0; x < imageData.width; x++) {               imageData.setAlpha(x, y, (y + alpha) % 256);             }           }           break;         default:           break;         }       }       displayImage(imageData);     } finally {       shell.setCursor(null);       imageCanvas.setCursor(crossCursor);       waitCursor.dispose();     }   }   void menuOpenFile() {     animate = false; // stop any animation in progress     resetScaleCombos();     // Get the user to choose an image file.     FileDialog fileChooser = new FileDialog(shell, SWT.OPEN);     if (lastPath != null)       fileChooser.setFilterPath(lastPath);     fileChooser.setFilterExtensions(new String[] {         "*.bmp; *.gif; *.ico; *.jpg; *.pcx; *.png; *.tif", "*.bmp",         "*.gif", "*.ico", "*.jpg", "*.pcx", "*.png", "*.tif" });     fileChooser.setFilterNames(new String[] {         "All_images"             + " (bmp, gif, ico, jpg, pcx, png, tif)",         "BMP (*.bmp)", "GIF (*.gif)", "ICO (*.ico)", "JPEG (*.jpg)",         "PCX (*.pcx)", "PNG (*.png)", "TIFF (*.tif)" });     String filename = fileChooser.open();     lastPath = fileChooser.getFilterPath();     if (filename == null)       return;     Cursor waitCursor = new Cursor(display, SWT.CURSOR_WAIT);     shell.setCursor(waitCursor);     imageCanvas.setCursor(waitCursor);     try {       loader = new ImageLoader();       if (incremental) {         // Prepare to handle incremental events.         loader.addImageLoaderListener(new ImageLoaderListener() {           public void imageDataLoaded(ImageLoaderEvent event) {             incrementalDataLoaded(event);           }         });         incrementalThreadStart();       }       // Read the new image(s) from the chosen file.       long startTime = System.currentTimeMillis();       imageDataArray = loader.load(filename);       loadTime = System.currentTimeMillis() - startTime;       if (imageDataArray.length > 0) {         // Cache the filename.         currentName = filename;         fileName = filename;         // If there are multiple images in the file (typically GIF)         // then enable the Previous, Next and Animate buttons.         previousButton.setEnabled(imageDataArray.length > 1);         nextButton.setEnabled(imageDataArray.length > 1);         animateButton.setEnabled(imageDataArray.length > 1             && loader.logicalScreenWidth > 0             && loader.logicalScreenHeight > 0);         // Display the first image in the file.         imageDataIndex = 0;         displayImage(imageDataArray[imageDataIndex]);         resetScrollBars();       }     } catch (SWTException e) {       showErrorDialog("Loading_lc", filename, e);     } catch (SWTError e) {       showErrorDialog("Loading_lc", filename, e);     } finally {       shell.setCursor(null);       imageCanvas.setCursor(crossCursor);       waitCursor.dispose();     }   }   void menuOpenURL() {     animate = false; // stop any animation in progress     resetScaleCombos();     // Get the user to choose an image URL.     TextPrompter textPrompter = new TextPrompter(shell,         SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);     textPrompter.setText("OpenURLDialog");     textPrompter.setMessage("EnterURL");     String urlname = textPrompter.open();     if (urlname == null)       return;     Cursor waitCursor = new Cursor(display, SWT.CURSOR_WAIT);     shell.setCursor(waitCursor);     imageCanvas.setCursor(waitCursor);     try {       URL url = new URL(urlname);       InputStream stream = url.openStream();       loader = new ImageLoader();       if (incremental) {         // Prepare to handle incremental events.         loader.addImageLoaderListener(new ImageLoaderListener() {           public void imageDataLoaded(ImageLoaderEvent event) {             incrementalDataLoaded(event);           }         });         incrementalThreadStart();       }       // Read the new image(s) from the chosen URL.       long startTime = System.currentTimeMillis();       imageDataArray = loader.load(stream);       stream.close();       loadTime = System.currentTimeMillis() - startTime;       if (imageDataArray.length > 0) {         currentName = urlname;         fileName = null;         // If there are multiple images (typically GIF)         // then enable the Previous, Next and Animate buttons.         previousButton.setEnabled(imageDataArray.length > 1);         nextButton.setEnabled(imageDataArray.length > 1);         animateButton.setEnabled(imageDataArray.length > 1             && loader.logicalScreenWidth > 0             && loader.logicalScreenHeight > 0);         // Display the first image.         imageDataIndex = 0;         displayImage(imageDataArray[imageDataIndex]);         resetScrollBars();       }     } catch (Exception e) {       showErrorDialog("Loading", urlname, e);     } finally {       shell.setCursor(null);       imageCanvas.setCursor(crossCursor);       waitCursor.dispose();     }   }   /*    * Called to start a thread that draws incremental images as they are    * loaded.    */   void incrementalThreadStart() {     incrementalEvents = new Vector();     incrementalThread = new Thread("Incremental") {       public void run() {         // Draw the first ImageData increment.         while (incrementalEvents != null) {           // Synchronize so we don't try to remove when the vector is           // null.           synchronized (ImageAnalyzer.this) {             if (incrementalEvents != null) {               if (incrementalEvents.size() > 0) {                 ImageLoaderEvent event = (ImageLoaderEvent) incrementalEvents                     .remove(0);                 if (image != null)                   image.dispose();                 image = new Image(display, event.imageData);                 imageData = event.imageData;                 imageCanvasGC.drawImage(image, 0, 0,                     imageData.width, imageData.height,                     imageData.x, imageData.y,                     imageData.width, imageData.height);               } else {                 yield();               }             }           }         }         display.wake();       }     };     incrementalThread.setDaemon(true);     incrementalThread.start();   }   /*    * Called when incremental image data has been loaded, for example, for    * interlaced GIF/PNG or progressive JPEG.    */   void incrementalDataLoaded(ImageLoaderEvent event) {     // Synchronize so that we do not try to add while     // the incremental drawing thread is removing.     synchronized (this) {       incrementalEvents.addElement(event);     }   }   void menuSave() {     if (image == null)       return;     animate = false; // stop any animation in progress     // If the image file type is unknown, we can't 'Save',     // so we have to use 'Save As...'.     if (imageData.type == SWT.IMAGE_UNDEFINED || fileName == null) {       menuSaveAs();       return;     }     Cursor waitCursor = new Cursor(display, SWT.CURSOR_WAIT);     shell.setCursor(waitCursor);     imageCanvas.setCursor(waitCursor);     try {       // Save the current image to the current file.       loader.data = new ImageData[] { imageData };       loader.save(fileName, imageData.type);     } catch (SWTException e) {       showErrorDialog("Saving_lc", fileName, e);     } catch (SWTError e) {       showErrorDialog("Saving_lc", fileName, e);     } finally {       shell.setCursor(null);       imageCanvas.setCursor(crossCursor);       waitCursor.dispose();     }   }   void menuSaveAs() {     if (image == null)       return;     animate = false; // stop any animation in progress     // Get the user to choose a file name and type to save.     FileDialog fileChooser = new FileDialog(shell, SWT.SAVE);     fileChooser.setFilterPath(lastPath);     if (fileName != null) {       String name = fileName;       int nameStart = name.lastIndexOf(java.io.File.separatorChar);       if (nameStart > -1) {         name = name.substring(nameStart + 1);       }       fileChooser.setFileName(name);     }     fileChooser.setFilterExtensions(new String[] { "*.bmp", "*.gif",         "*.ico", "*.jpg", "*.png" });     fileChooser.setFilterNames(new String[] { "BMP (*.bmp)", "GIF (*.gif)",         "ICO (*.ico)", "JPEG (*.jpg)", "PNG (*.png)" });     String filename = fileChooser.open();     lastPath = fileChooser.getFilterPath();     if (filename == null)       return;     // Figure out what file type the user wants saved.     // We need to rely on the file extension because FileDialog     // does not have API for asking what filter type was selected.     int filetype = determineFileType(filename);     if (filetype == SWT.IMAGE_UNDEFINED) {       MessageBox box = new MessageBox(shell, SWT.ICON_ERROR);       box.setMessage(createMsg("Unknown_extension",           filename.substring(filename.lastIndexOf('.') + 1)));       box.open();       return;     }     if (new java.io.File(filename).exists()) {       MessageBox box = new MessageBox(shell, SWT.ICON_QUESTION | SWT.OK           | SWT.CANCEL);       box.setMessage(createMsg("Overwrite", filename));       if (box.open() == SWT.CANCEL)         return;     }     Cursor waitCursor = new Cursor(display, SWT.CURSOR_WAIT);     shell.setCursor(waitCursor);     imageCanvas.setCursor(waitCursor);     try {       // Save the current image to the specified file.       loader.data = new ImageData[] { imageData };       loader.save(filename, filetype);       // Update the shell title and file type label,       // and use the new file.       fileName = filename;       shell.setText(createMsg("Analyzer_on", filename));       typeLabel.setText(createMsg("Type_string",           fileTypeString(filetype)));     } catch (SWTException e) {       showErrorDialog("Saving_lc", filename, e);     } catch (SWTError e) {       showErrorDialog("Saving_lc", filename, e);     } finally {       shell.setCursor(null);       imageCanvas.setCursor(crossCursor);       waitCursor.dispose();     }   }   void menuSaveMaskAs() {     if (image == null || !showMask)       return;     if (imageData.getTransparencyType() == SWT.TRANSPARENCY_NONE)       return;     animate = false; // stop any animation in progress     // Get the user to choose a file name and type to save.     FileDialog fileChooser = new FileDialog(shell, SWT.SAVE);     fileChooser.setFilterPath(lastPath);     if (fileName != null)       fileChooser.setFileName(fileName);     fileChooser.setFilterExtensions(new String[] { "*.bmp", "*.gif",         "*.ico", "*.jpg", "*.png" });     fileChooser.setFilterNames(new String[] { "BMP (*.bmp)", "GIF (*.gif)",         "ICO (*.ico)", "JPEG (*.jpg)", "PNG (*.png)" });     String filename = fileChooser.open();     lastPath = fileChooser.getFilterPath();     if (filename == null)       return;     // Figure out what file type the user wants saved.     // We need to rely on the file extension because FileDialog     // does not have API for asking what filter type was selected.     int filetype = determineFileType(filename);     if (filetype == SWT.IMAGE_UNDEFINED) {       MessageBox box = new MessageBox(shell, SWT.ICON_ERROR);       box.setMessage(createMsg("Unknown_extension",           filename.substring(filename.lastIndexOf('.') + 1)));       box.open();       return;     }     if (new java.io.File(filename).exists()) {       MessageBox box = new MessageBox(shell, SWT.ICON_QUESTION | SWT.OK           | SWT.CANCEL);       box.setMessage(createMsg("Overwrite", filename));       if (box.open() == SWT.CANCEL)         return;     }     Cursor waitCursor = new Cursor(display, SWT.CURSOR_WAIT);     shell.setCursor(waitCursor);     imageCanvas.setCursor(waitCursor);     try {       // Save the mask of the current image to the specified file.       ImageData maskImageData = imageData.getTransparencyMask();       loader.data = new ImageData[] { maskImageData };       loader.save(filename, filetype);     } catch (SWTException e) {       showErrorDialog("Saving_lc", filename, e);     } catch (SWTError e) {       showErrorDialog("Saving_lc", filename, e);     } finally {       shell.setCursor(null);       imageCanvas.setCursor(crossCursor);       waitCursor.dispose();     }   }   void menuPrint() {     if (image == null)       return;     try {       // Ask the user to specify the printer.       PrintDialog dialog = new PrintDialog(shell, SWT.NULL);       PrinterData printerData = dialog.open();       if (printerData == null)         return;       Printer printer = new Printer(printerData);       Point screenDPI = display.getDPI();       Point printerDPI = printer.getDPI();       int scaleFactor = printerDPI.x / screenDPI.x;       Rectangle trim = printer.computeTrim(0, 0, 0, 0);       if (printer.startJob(currentName)) {         if (printer.startPage()) {           GC gc = new GC(printer);           int transparentPixel = imageData.transparentPixel;           if (transparentPixel != -1 && !transparent) {             imageData.transparentPixel = -1;           }           Image printerImage = new Image(printer, imageData);           gc.drawImage(printerImage, 0, 0, imageData.width,               imageData.height, -trim.x, -trim.y, scaleFactor                   * imageData.width, scaleFactor                   * imageData.height);           if (transparentPixel != -1 && !transparent) {             imageData.transparentPixel = transparentPixel;           }           printerImage.dispose();           gc.dispose();           printer.endPage();         }         printer.endJob();       }       printer.dispose();     } catch (SWTError e) {       MessageBox box = new MessageBox(shell, SWT.ICON_ERROR);       box.setMessage("Printing_error" + e.getMessage());       box.open();     }   }   void menuReopen() {     if (currentName == null)       return;     animate = false; // stop any animation in progress     resetScrollBars();     resetScaleCombos();     Cursor waitCursor = new Cursor(display, SWT.CURSOR_WAIT);     shell.setCursor(waitCursor);     imageCanvas.setCursor(waitCursor);     try {       loader = new ImageLoader();       long startTime = System.currentTimeMillis();       ImageData[] newImageData;       if (fileName == null) {         URL url = new URL(currentName);         InputStream stream = url.openStream();         newImageData = loader.load(stream);         stream.close();       } else {         newImageData = loader.load(fileName);       }       loadTime = System.currentTimeMillis() - startTime;       imageDataIndex = 0;       displayImage(newImageData[imageDataIndex]);     } catch (Exception e) {       showErrorDialog("Reloading", currentName, e);     } finally {       shell.setCursor(null);       imageCanvas.setCursor(crossCursor);       waitCursor.dispose();     }   }   void changeBackground() {     String background = backgroundCombo.getText();     if (background.equals("White")) {       imageCanvas.setBackground(whiteColor);     } else if (background.equals("Black")) {       imageCanvas.setBackground(blackColor);     } else if (background.equals("Red")) {       imageCanvas.setBackground(redColor);     } else if (background.equals("Green")) {       imageCanvas.setBackground(greenColor);     } else if (background.equals("Blue")) {       imageCanvas.setBackground(blueColor);     } else {       imageCanvas.setBackground(null);     }   }   /*    * Called when the ScaleX combo selection changes.    */   void scaleX() {     try {       xscale = Float.parseFloat(scaleXCombo.getText());     } catch (NumberFormatException e) {       xscale = 1;       scaleXCombo.select(scaleXCombo.indexOf("1"));     }     if (image != null) {       resizeScrollBars();       imageCanvas.redraw();     }   }   /*    * Called when the ScaleY combo selection changes.    */   void scaleY() {     try {       yscale = Float.parseFloat(scaleYCombo.getText());     } catch (NumberFormatException e) {       yscale = 1;       scaleYCombo.select(scaleYCombo.indexOf("1"));     }     if (image != null) {       resizeScrollBars();       imageCanvas.redraw();     }   }   /*    * Called when the Alpha combo selection changes.    */   void alpha() {     try {       alpha = Integer.parseInt(alphaCombo.getText());     } catch (NumberFormatException e) {       alphaCombo.select(alphaCombo.indexOf("255"));       alpha = 255;     }   }   /*    * Called when the mouse moves in the image canvas. Show the color of the    * image at the point under the mouse.    */   void showColorAt(int mx, int my) {     int x = mx - imageData.x - ix;     int y = my - imageData.y - iy;     showColorForPixel(x, y);   }   /*    * Called when a mouse down or key press is detected in the data text. Show    * the color of the pixel at the caret position in the data text.    */   void showColorForData() {     int delimiterLength = dataText.getLineDelimiter().length();     int charactersPerLine = 6 + 3 * imageData.bytesPerLine         + delimiterLength;     int position = dataText.getCaretOffset();     int y = position / charactersPerLine;     if ((position - y * charactersPerLine) < 6         || ((y + 1) * charactersPerLine - position) <= delimiterLength) {       statusLabel.setText("");       return;     }     int dataPosition = position - 6 * (y + 1) - delimiterLength * y;     int byteNumber = dataPosition / 3;     int where = dataPosition - byteNumber * 3;     int xByte = byteNumber % imageData.bytesPerLine;     int x = -1;     int depth = imageData.depth;     if (depth == 1) { // 8 pixels per byte (can only show 3 of 8)       if (where == 0)         x = xByte * 8;       if (where == 1)         x = xByte * 8 + 3;       if (where == 2)         x = xByte * 8 + 7;     }     if (depth == 2) { // 4 pixels per byte (can only show 3 of 4)       if (where == 0)         x = xByte * 4;       if (where == 1)         x = xByte * 4 + 1;       if (where == 2)         x = xByte * 4 + 3;     }     if (depth == 4) { // 2 pixels per byte       if (where == 0)         x = xByte * 2;       if (where == 1)         x = xByte * 2;       if (where == 2)         x = xByte * 2 + 1;     }     if (depth == 8) { // 1 byte per pixel       x = xByte;     }     if (depth == 16) { // 2 bytes per pixel       x = xByte / 2;     }     if (depth == 24) { // 3 bytes per pixel       x = xByte / 3;     }     if (depth == 32) { // 4 bytes per pixel       x = xByte / 4;     }     if (x != -1) {       showColorForPixel(x, y);     } else {       statusLabel.setText("");     }   }   /*    * Set the status label to show color information for the specified pixel in    * the image.    */   void showColorForPixel(int x, int y) {     if (x >= 0 && x < imageData.width && y >= 0 && y < imageData.height) {       int pixel = imageData.getPixel(x, y);       RGB rgb = imageData.palette.getRGB(pixel);       Object[] args = { new Integer(x), new Integer(y),           new Integer(pixel), Integer.toHexString(pixel), rgb };       if (pixel == imageData.transparentPixel) {         statusLabel.setText(createMsg("Color_at_trans", args));       } else {         statusLabel.setText(createMsg("Color_at",             args));       }     } else {       statusLabel.setText("");     }   }   /*    * Called when the Animate button is pressed.    */   void animate() {     animate = !animate;     if (animate && image != null && imageDataArray.length > 1) {       animateThread = new Thread("Animation") {         public void run() {           // Pre-animation widget setup.           preAnimation();           // Animate.           try {             animateLoop();           } catch (final SWTException e) {             display.syncExec(new Runnable() {               public void run() {                 showErrorDialog(createMsg("Creating_image",                     new Integer(imageDataIndex + 1)),                     currentName, e);               }             });           }           // Post animation widget reset.           postAnimation();         }       };       animateThread.setDaemon(true);       animateThread.start();     }   }   /*    * Loop through all of the images in a multi-image file and display them one    * after another.    */   void animateLoop() {     // Create an off-screen image to draw on, and a GC to draw with.     // Both are disposed after the animation.     Image offScreenImage = new Image(display, loader.logicalScreenWidth,         loader.logicalScreenHeight);     GC offScreenImageGC = new GC(offScreenImage);     try {       // Use syncExec to get the background color of the imageCanvas.       display.syncExec(new Runnable() {         public void run() {           canvasBackground = imageCanvas.getBackground();         }       });       // Fill the off-screen image with the background color of the       // canvas.       offScreenImageGC.setBackground(canvasBackground);       offScreenImageGC.fillRectangle(0, 0, loader.logicalScreenWidth,           loader.logicalScreenHeight);       // Draw the current image onto the off-screen image.       offScreenImageGC.drawImage(image, 0, 0, imageData.width,           imageData.height, imageData.x, imageData.y,           imageData.width, imageData.height);       int repeatCount = loader.repeatCount;       while (animate && (loader.repeatCount == 0 || repeatCount > 0)) {         if (imageData.disposalMethod == SWT.DM_FILL_BACKGROUND) {           // Fill with the background color before drawing.           Color bgColor = null;           int backgroundPixel = loader.backgroundPixel;           if (showBackground && backgroundPixel != -1) {             // Fill with the background color.             RGB backgroundRGB = imageData.palette                 .getRGB(backgroundPixel);             bgColor = new Color(null, backgroundRGB);           }           try {             offScreenImageGC                 .setBackground(bgColor != null ? bgColor                     : canvasBackground);             offScreenImageGC.fillRectangle(imageData.x,                 imageData.y, imageData.width, imageData.height);           } finally {             if (bgColor != null)               bgColor.dispose();           }         } else if (imageData.disposalMethod == SWT.DM_FILL_PREVIOUS) {           // Restore the previous image before drawing.           offScreenImageGC.drawImage(image, 0, 0, imageData.width,               imageData.height, imageData.x, imageData.y,               imageData.width, imageData.height);         }         // Get the next image data.         imageDataIndex = (imageDataIndex + 1) % imageDataArray.length;         imageData = imageDataArray[imageDataIndex];         image.dispose();         image = new Image(display, imageData);         // Draw the new image data.         offScreenImageGC.drawImage(image, 0, 0, imageData.width,             imageData.height, imageData.x, imageData.y,             imageData.width, imageData.height);         // Draw the off-screen image to the screen.         imageCanvasGC.drawImage(offScreenImage, 0, 0);         // Sleep for the specified delay time before drawing again.         try {           Thread.sleep(visibleDelay(imageData.delayTime * 10));         } catch (InterruptedException e) {         }         // If we have just drawn the last image in the set,         // then decrement the repeat count.         if (imageDataIndex == imageDataArray.length - 1)           repeatCount--;       }     } finally {       offScreenImage.dispose();       offScreenImageGC.dispose();     }   }   /*    * Pre animation setup.    */   void preAnimation() {     display.syncExec(new Runnable() {       public void run() {         // Change the label of the Animate button to 'Stop'.         animateButton.setText("Stop");         // Disable anything we don't want the user         // to select during the animation.         previousButton.setEnabled(false);         nextButton.setEnabled(false);         backgroundCombo.setEnabled(false);         scaleXCombo.setEnabled(false);         scaleYCombo.setEnabled(false);         alphaCombo.setEnabled(false);         incrementalCheck.setEnabled(false);         transparentCheck.setEnabled(false);         maskCheck.setEnabled(false);         // leave backgroundCheck enabled         // Reset the scale combos and scrollbars.         resetScaleCombos();         resetScrollBars();       }     });   }   /*    * Post animation reset.    */   void postAnimation() {     display.syncExec(new Runnable() {       public void run() {         // Enable anything we disabled before the animation.         previousButton.setEnabled(true);         nextButton.setEnabled(true);         backgroundCombo.setEnabled(true);         scaleXCombo.setEnabled(true);         scaleYCombo.setEnabled(true);         alphaCombo.setEnabled(true);         incrementalCheck.setEnabled(true);         transparentCheck.setEnabled(true);         maskCheck.setEnabled(true);         // Reset the label of the Animate button.         animateButton.setText("Animate");         if (animate) {           // If animate is still true, we finished the           // full number of repeats. Leave the image as-is.           animate = false;         } else {           // Redisplay the current image and its palette.           displayImage(imageDataArray[imageDataIndex]);         }       }     });   }   /*    * Called when the Previous button is pressed. Display the previous image in    * a multi-image file.    */   void previous() {     if (image != null && imageDataArray.length > 1) {       if (imageDataIndex == 0) {         imageDataIndex = imageDataArray.length;       }       imageDataIndex = imageDataIndex - 1;       displayImage(imageDataArray[imageDataIndex]);     }   }   /*    * Called when the Next button is pressed. Display the next image in a    * multi-image file.    */   void next() {     if (image != null && imageDataArray.length > 1) {       imageDataIndex = (imageDataIndex + 1) % imageDataArray.length;       displayImage(imageDataArray[imageDataIndex]);     }   }   void displayImage(ImageData newImageData) {     if (incremental && incrementalThread != null) {       // Tell the incremental thread to stop drawing.       synchronized (this) {         incrementalEvents = null;       }       // Wait until the incremental thread is done.       while (incrementalThread.isAlive()) {         if (!display.readAndDispatch())           display.sleep();       }     }     // Dispose of the old image, if there was one.     if (image != null)       image.dispose();     try {       // Cache the new image and imageData.       image = new Image(display, newImageData);       imageData = newImageData;     } catch (SWTException e) {       showErrorDialog("Creating_from" + " ",           currentName, e);       image = null;       return;     }     // Update the widgets with the new image info.     String string = createMsg("Analyzer_on", currentName);     shell.setText(string);     if (imageDataArray.length > 1) {       string = createMsg("Type_index", new Object[] {           fileTypeString(imageData.type),           new Integer(imageDataIndex + 1),           new Integer(imageDataArray.length) });     } else {       string = createMsg("Type_string",           fileTypeString(imageData.type));     }     typeLabel.setText(string);     string = createMsg("Size_value", new Object[] {         new Integer(imageData.width), new Integer(imageData.height) });     sizeLabel.setText(string);     string = createMsg("Depth_value", new Integer(         imageData.depth));     depthLabel.setText(string);     string = createMsg("Transparent_pixel_value",         pixelInfo(imageData.transparentPixel));     transparentPixelLabel.setText(string);     string = createMsg("Time_to_load_value", new Long(         loadTime));     timeToLoadLabel.setText(string);     string = createMsg("Animation_size_value",         new Object[] { new Integer(loader.logicalScreenWidth),             new Integer(loader.logicalScreenHeight) });     screenSizeLabel.setText(string);     string = createMsg("Background_pixel_value",         pixelInfo(loader.backgroundPixel));     backgroundPixelLabel.setText(string);     string = createMsg("Image_location_value",         new Object[] { new Integer(imageData.x),             new Integer(imageData.y) });     locationLabel.setText(string);     string = createMsg("Disposal_value", new Object[] {         new Integer(imageData.disposalMethod),         disposalString(imageData.disposalMethod) });     disposalMethodLabel.setText(string);     int delay = imageData.delayTime * 10;     int delayUsed = visibleDelay(delay);     if (delay != delayUsed) {       string = createMsg("Delay_value", new Object[] {           new Integer(delay), new Integer(delayUsed) });     } else {       string = createMsg("Delay_used", new Integer(           delay));     }     delayTimeLabel.setText(string);     if (loader.repeatCount == 0) {       string = createMsg("Repeats_forever",           new Integer(loader.repeatCount));     } else {       string = createMsg("Repeats_value", new Integer(           loader.repeatCount));     }     repeatCountLabel.setText(string);     if (imageData.palette.isDirect) {       string = "Palette_direct";     } else {       string = createMsg("Palette_value", new Integer(           imageData.palette.getRGBs().length));     }     paletteLabel.setText(string);     string = createMsg("Pixel_data_value",         new Object[] { new Integer(imageData.bytesPerLine),             new Integer(imageData.scanlinePad),             depthInfo(imageData.depth) });     dataLabel.setText(string);     String data = dataHexDump(dataText.getLineDelimiter());     dataText.setText(data);     // bold the first column all the way down     int index = 0;     while ((index = data.indexOf(':', index + 1)) != -1)       dataText.setStyleRange(new StyleRange(index - INDEX_DIGITS,           INDEX_DIGITS, dataText.getForeground(), dataText               .getBackground(), SWT.BOLD));     statusLabel.setText("");     // Redraw both canvases.     paletteCanvas.redraw();     imageCanvas.redraw();   }   void paintImage(PaintEvent event) {     Image paintImage = image;     int transparentPixel = imageData.transparentPixel;     if (transparentPixel != -1 && !transparent) {       imageData.transparentPixel = -1;       paintImage = new Image(display, imageData);     }     int w = Math.round(imageData.width * xscale);     int h = Math.round(imageData.height * yscale);     event.gc.drawImage(paintImage, 0, 0, imageData.width, imageData.height,         ix + imageData.x, iy + imageData.y, w, h);     if (showMask         && (imageData.getTransparencyType() != SWT.TRANSPARENCY_NONE)) {       ImageData maskImageData = imageData.getTransparencyMask();       Image maskImage = new Image(display, maskImageData);       event.gc.drawImage(maskImage, 0, 0, imageData.width,           imageData.height, w + 10 + ix + imageData.x, iy               + imageData.y, w, h);       maskImage.dispose();     }     if (transparentPixel != -1 && !transparent) {       imageData.transparentPixel = transparentPixel;       paintImage.dispose();     }   }   void paintPalette(PaintEvent event) {     GC gc = event.gc;     gc.fillRectangle(paletteCanvas.getClientArea());     if (imageData.palette.isDirect) {       // For a direct palette, display the masks.       int y = py + 10;       int xTab = 50;       gc.drawString("rMsk", 10, y, true);       gc.drawString(toHex4ByteString(imageData.palette.redMask), xTab, y,           true);       gc.drawString("gMsk", 10, y += 12, true);       gc.drawString(toHex4ByteString(imageData.palette.greenMask), xTab,           y, true);       gc.drawString("bMsk", 10, y += 12, true);       gc.drawString(toHex4ByteString(imageData.palette.blueMask), xTab,           y, true);       gc.drawString("rShf", 10, y += 12, true);       gc.drawString(Integer.toString(imageData.palette.redShift), xTab,           y, true);       gc.drawString("gShf", 10, y += 12, true);       gc.drawString(Integer.toString(imageData.palette.greenShift), xTab,           y, true);       gc.drawString("bShf", 10, y += 12, true);       gc.drawString(Integer.toString(imageData.palette.blueShift), xTab,           y, true);     } else {       // For an indexed palette, display the palette colors and indices.       RGB[] rgbs = imageData.palette.getRGBs();       if (rgbs != null) {         int xTab1 = 40, xTab2 = 100;         for (int i = 0; i < rgbs.length; i++) {           int y = (i + 1) * 10 + py;           gc.drawString(String.valueOf(i), 10, y, true);           gc.drawString(toHexByteString(rgbs[i].red)               + toHexByteString(rgbs[i].green)               + toHexByteString(rgbs[i].blue), xTab1, y, true);           Color color = new Color(display, rgbs[i]);           gc.setBackground(color);           gc.fillRectangle(xTab2, y + 2, 10, 10);           color.dispose();         }       }     }   }   void resizeShell(ControlEvent event) {     if (image == null || shell.isDisposed())       return;     resizeScrollBars();   }   // Reset the scale combos to 1.   void resetScaleCombos() {     xscale = 1;     yscale = 1;     scaleXCombo.select(scaleXCombo.indexOf("1"));     scaleYCombo.select(scaleYCombo.indexOf("1"));   }   // Reset the scroll bars to 0.   void resetScrollBars() {     if (image == null)       return;     ix = 0;     iy = 0;     py = 0;     resizeScrollBars();     imageCanvas.getHorizontalBar().setSelection(0);     imageCanvas.getVerticalBar().setSelection(0);     paletteCanvas.getVerticalBar().setSelection(0);   }   void resizeScrollBars() {     // Set the max and thumb for the image canvas scroll bars.     ScrollBar horizontal = imageCanvas.getHorizontalBar();     ScrollBar vertical = imageCanvas.getVerticalBar();     Rectangle canvasBounds = imageCanvas.getClientArea();     int width = Math.round(imageData.width * xscale);     if (width > canvasBounds.width) {       // The image is wider than the canvas.       horizontal.setEnabled(true);       horizontal.setMaximum(width);       horizontal.setThumb(canvasBounds.width);       horizontal.setPageIncrement(canvasBounds.width);     } else {       // The canvas is wider than the image.       horizontal.setEnabled(false);       if (ix != 0) {         // Make sure the image is completely visible.         ix = 0;         imageCanvas.redraw();       }     }     int height = Math.round(imageData.height * yscale);     if (height > canvasBounds.height) {       // The image is taller than the canvas.       vertical.setEnabled(true);       vertical.setMaximum(height);       vertical.setThumb(canvasBounds.height);       vertical.setPageIncrement(canvasBounds.height);     } else {       // The canvas is taller than the image.       vertical.setEnabled(false);       if (iy != 0) {         // Make sure the image is completely visible.         iy = 0;         imageCanvas.redraw();       }     }     // Set the max and thumb for the palette canvas scroll bar.     vertical = paletteCanvas.getVerticalBar();     if (imageData.palette.isDirect) {       vertical.setEnabled(false);     } else { // indexed palette       canvasBounds = paletteCanvas.getClientArea();       int paletteHeight = imageData.palette.getRGBs().length * 10 + 20; // 10                                         // pixels                                         // each                                         // index                                         // + 20                                         // for                                         // margins.       vertical.setEnabled(true);       vertical.setMaximum(paletteHeight);       vertical.setThumb(canvasBounds.height);       vertical.setPageIncrement(canvasBounds.height);     }   }   /*    * Called when the image canvas' horizontal scrollbar is selected.    */   void scrollHorizontally(ScrollBar scrollBar) {     if (image == null)       return;     Rectangle canvasBounds = imageCanvas.getClientArea();     int width = Math.round(imageData.width * xscale);     int height = Math.round(imageData.height * yscale);     if (width > canvasBounds.width) {       // Only scroll if the image is bigger than the canvas.       int x = -scrollBar.getSelection();       if (x + width < canvasBounds.width) {         // Don't scroll past the end of the image.         x = canvasBounds.width - width;       }       imageCanvas.scroll(x, iy, ix, iy, width, height, false);       ix = x;     }   }   /*    * Called when the image canvas' vertical scrollbar is selected.    */   void scrollVertically(ScrollBar scrollBar) {     if (image == null)       return;     Rectangle canvasBounds = imageCanvas.getClientArea();     int width = Math.round(imageData.width * xscale);     int height = Math.round(imageData.height * yscale);     if (height > canvasBounds.height) {       // Only scroll if the image is bigger than the canvas.       int y = -scrollBar.getSelection();       if (y + height < canvasBounds.height) {         // Don't scroll past the end of the image.         y = canvasBounds.height - height;       }       imageCanvas.scroll(ix, y, ix, iy, width, height, false);       iy = y;     }   }   /*    * Called when the palette canvas' vertical scrollbar is selected.    */   void scrollPalette(ScrollBar scrollBar) {     if (image == null)       return;     Rectangle canvasBounds = paletteCanvas.getClientArea();     int paletteHeight = imageData.palette.getRGBs().length * 10 + 20;     if (paletteHeight > canvasBounds.height) {       // Only scroll if the palette is bigger than the canvas.       int y = -scrollBar.getSelection();       if (y + paletteHeight < canvasBounds.height) {         // Don't scroll past the end of the palette.         y = canvasBounds.height - paletteHeight;       }       paletteCanvas.scroll(0, y, 0, py, paletteWidth, paletteHeight,           false);       py = y;     }   }   /*    * Return a String containing a line-by-line dump of the data in the current    * imageData. The lineDelimiter parameter must be a string of length 1 or 2.    */   String dataHexDump(String lineDelimiter) {     if (image == null)       return "";     char[] dump = new char[imageData.height         * (6 + 3 * imageData.bytesPerLine + lineDelimiter.length())];     int index = 0;     for (int i = 0; i < imageData.data.length; i++) {       if (i % imageData.bytesPerLine == 0) {         int line = i / imageData.bytesPerLine;         dump[index++] = Character.forDigit(line / 1000 % 10, 10);         dump[index++] = Character.forDigit(line / 100 % 10, 10);         dump[index++] = Character.forDigit(line / 10 % 10, 10);         dump[index++] = Character.forDigit(line % 10, 10);         dump[index++] = ':';         dump[index++] = ' ';       }       byte b = imageData.data[i];       dump[index++] = Character.forDigit((b & 0xF0) >> 4, 16);       dump[index++] = Character.forDigit(b & 0x0F, 16);       dump[index++] = ' ';       if ((i + 1) % imageData.bytesPerLine == 0) {         dump[index++] = lineDelimiter.charAt(0);         if (lineDelimiter.length() > 1)           dump[index++] = lineDelimiter.charAt(1);       }     }     String result = "";     try {       result = new String(dump);     } catch (OutOfMemoryError e) {       /* Too much data to display in the text widget - truncate at 4M. */       result = new String(dump, 0, 4 * 1024 * 1024)           + "\n ...data dump truncated at 4M...";     }     return result;   }   /*    * Open an error dialog displaying the specified information.    */   void showErrorDialog(String operation, String filename, Throwable e) {     MessageBox box = new MessageBox(shell, SWT.ICON_ERROR);     String message = createMsg("Error", new String[] {         operation, filename });     String errorMessage = "";     if (e != null) {       if (e instanceof SWTException) {         SWTException swte = (SWTException) e;         errorMessage = swte.getMessage();         if (swte.throwable != null) {           errorMessage += ":\n" + swte.throwable.toString();         }       } else if (e instanceof SWTError) {         SWTError swte = (SWTError) e;         errorMessage = swte.getMessage();         if (swte.throwable != null) {           errorMessage += ":\n" + swte.throwable.toString();         }       } else {         errorMessage = e.toString();       }     }     box.setMessage(message + errorMessage);     box.open();   }   /*    * Open a dialog asking the user for more information on the type of BMP    * file to save.    */   int showBMPDialog() {     final int[] bmpType = new int[1];     bmpType[0] = SWT.IMAGE_BMP;     SelectionListener radioSelected = new SelectionAdapter() {       public void widgetSelected(SelectionEvent event) {         Button radio = (Button) event.widget;         if (radio.getSelection())           bmpType[0] = ((Integer) radio.getData()).intValue();       }     };     // need to externalize strings     final Shell dialog = new Shell(shell, SWT.DIALOG_TRIM);     dialog.setText("Save_as");     dialog.setLayout(new GridLayout());     Label label = new Label(dialog, SWT.NONE);     label.setText("Save_as");     Button radio = new Button(dialog, SWT.RADIO);     radio.setText("Save_as_type_no_compress");     radio.setSelection(true);     radio.setData(new Integer(SWT.IMAGE_BMP));     radio.addSelectionListener(radioSelected);     radio = new Button(dialog, SWT.RADIO);     radio.setText("Save_as_type_rle_compress");     radio.setData(new Integer(SWT.IMAGE_BMP_RLE));     radio.addSelectionListener(radioSelected);     radio = new Button(dialog, SWT.RADIO);     radio.setText("Save_as_type_os2");     radio.setData(new Integer(SWT.IMAGE_OS2_BMP));     radio.addSelectionListener(radioSelected);     label = new Label(dialog, SWT.SEPARATOR | SWT.HORIZONTAL);     label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));     Button ok = new Button(dialog, SWT.PUSH);     ok.setText("OK");     GridData data = new GridData();     data.horizontalAlignment = SWT.CENTER;     data.widthHint = 75;     ok.setLayoutData(data);     ok.addSelectionListener(new SelectionAdapter() {       public void widgetSelected(SelectionEvent e) {         dialog.close();       }     });     dialog.pack();     dialog.open();     while (!dialog.isDisposed()) {       if (!display.readAndDispatch())         display.sleep();     }     return bmpType[0];   }   /*    * Return a String describing how to analyze the bytes in the hex dump.    */   static String depthInfo(int depth) {     Object[] args = { new Integer(depth), "" };     switch (depth) {     case 1:       args[1] = createMsg("Multi_pixels", new Object[] {           new Integer(8), " [01234567]" });       break;     case 2:       args[1] = createMsg("Multi_pixels", new Object[] {           new Integer(4), "[00112233]" });       break;     case 4:       args[1] = createMsg("Multi_pixels", new Object[] {           new Integer(2), "[00001111]" });       break;     case 8:       args[1] = "One_byte";       break;     case 16:       args[1] = createMsg("Multi_bytes", new Integer(2));       break;     case 24:       args[1] = createMsg("Multi_bytes", new Integer(3));       break;     case 32:       args[1] = createMsg("Multi_bytes", new Integer(4));       break;     default:       args[1] = "Unsupported_lc";     }     return createMsg("Depth_info", args);   }   /*    * Return the specified number of milliseconds. If the specified number of    * milliseconds is too small to see a visual change, then return a higher    * number.    */   static int visibleDelay(int ms) {     if (ms < 20)       return ms + 30;     if (ms < 30)       return ms + 10;     return ms;   }   /*    * Return the specified byte value as a hex string, preserving leading 0's.    */   static String toHexByteString(int i) {     if (i <= 0x0f)       return "0" + Integer.toHexString(i);     return Integer.toHexString(i & 0xff);   }   /*    * Return the specified 4-byte value as a hex string, preserving leading    * 0's. (a bit 'brute force'... should probably use a loop...)    */   static String toHex4ByteString(int i) {     String hex = Integer.toHexString(i);     if (hex.length() == 1)       return "0000000" + hex;     if (hex.length() == 2)       return "000000" + hex;     if (hex.length() == 3)       return "00000" + hex;     if (hex.length() == 4)       return "0000" + hex;     if (hex.length() == 5)       return "000" + hex;     if (hex.length() == 6)       return "00" + hex;     if (hex.length() == 7)       return "0" + hex;     return hex;   }   /*    * Return a String describing the specified transparent or background pixel.    */   static String pixelInfo(int pixel) {     if (pixel == -1)       return pixel + " (" + "None_lc" + ")";     else       return pixel + " (0x" + Integer.toHexString(pixel) + ")";   }   /*    * Return a String describing the specified disposal method.    */   static String disposalString(int disposalMethod) {     switch (disposalMethod) {     case SWT.DM_FILL_NONE:       return "None_lc";     case SWT.DM_FILL_BACKGROUND:       return "Background_lc";     case SWT.DM_FILL_PREVIOUS:       return "Previous_lc";     }     return "Unspecified_lc";   }   /*    * Return a String describing the specified image file type.    */   String fileTypeString(int filetype) {     if (filetype == SWT.IMAGE_BMP)       return "BMP";     if (filetype == SWT.IMAGE_BMP_RLE)       return "RLE" + imageData.depth + " BMP";     if (filetype == SWT.IMAGE_OS2_BMP)       return "OS/2 BMP";     if (filetype == SWT.IMAGE_GIF)       return "GIF";     if (filetype == SWT.IMAGE_ICO)       return "ICO";     if (filetype == SWT.IMAGE_JPEG)       return "JPEG";     if (filetype == SWT.IMAGE_PNG)       return "PNG";     return "Unknown_ac";   }   /*    * Return the specified file's image type, based on its extension. Note that    * this is not a very robust way to determine image type, and it is only to    * be used in the absence of any better method.    */   int determineFileType(String filename) {     String ext = filename.substring(filename.lastIndexOf('.') + 1);     if (ext.equalsIgnoreCase("bmp")) {       return showBMPDialog();     }     if (ext.equalsIgnoreCase("gif"))       return SWT.IMAGE_GIF;     if (ext.equalsIgnoreCase("ico"))       return SWT.IMAGE_ICO;     if (ext.equalsIgnoreCase("jpg") || ext.equalsIgnoreCase("jpeg"))       return SWT.IMAGE_JPEG;     if (ext.equalsIgnoreCase("png"))       return SWT.IMAGE_PNG;     return SWT.IMAGE_UNDEFINED;   }   static String createMsg(String msg, Object[] args) {     MessageFormat formatter = new MessageFormat(msg);     return formatter.format(args);   }   static String createMsg(String msg, Object arg) {     MessageFormat formatter = new MessageFormat(msg);     return formatter.format(new Object[] { arg });   } }