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 });
}
}