package org.eclipse.swt.examples.imageexample; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved | |
*/ | |
import org.eclipse.swt.*; | |
import org.eclipse.swt.widgets.*; | |
import org.eclipse.swt.events.*; | |
import org.eclipse.swt.layout.*; | |
import org.eclipse.swt.graphics.*; | |
import java.util.*; | |
import java.text.MessageFormat; | |
public class ImageAnalyzer { | |
static ResourceBundle bundle = ResourceBundle.getBundle("examples_images"); | |
Shell shell; | |
Canvas imageCanvas, paletteCanvas; | |
Label typeLabel, sizeLabel, depthLabel, transparentPixelLabel, | |
screenSizeLabel, backgroundPixelLabel, locationLabel, | |
disposalMethodLabel, delayTimeLabel, repeatCountLabel, | |
paletteLabel, dataLabel, statusLabel; | |
Combo backgroundCombo, scaleXCombo, scaleYCombo, alphaCombo; | |
Button incrementalCheck, transparentCheck, maskCheck, backgroundCheck; | |
Button previousButton, nextButton, animateButton; | |
Text dataText; | |
Display display; | |
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 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 | |
static final int ALPHA_CONSTANT = 0; | |
static final int ALPHA_X = 1; | |
static final int ALPHA_Y = 2; | |
public static void main(String [] args) { | |
ImageAnalyzer imageAnalyzer = new ImageAnalyzer(); | |
imageAnalyzer.open(); | |
} | |
void open() { | |
// Create a window and set its title. | |
shell = new Shell(); | |
shell.setText(bundle.getString("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 event) { | |
if (animate && animateThread != null) { | |
// Stop the animation and wait for the | |
// thread to die before disposing the shell. | |
animate = false; | |
while (animateThread.isAlive()) { | |
if (!display.readAndDispatch()) display.sleep(); | |
} | |
} | |
} | |
}); | |
// Create colors and fonts. | |
display = shell.getDisplay(); | |
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. | |
Menu menuBar = createMenuBar(); | |
createWidgets(); | |
shell.pack(); | |
// Create a GC for drawing. | |
imageCanvasGC = new GC(imageCanvas); | |
// Open the window, and run an event loop until the window is closed. | |
shell.open(); | |
while (!shell.isDisposed()) | |
if (!display.readAndDispatch()) display.sleep(); | |
// Clean up. | |
if (image != null) | |
image.dispose(); | |
whiteColor.dispose(); | |
blackColor.dispose(); | |
redColor.dispose(); | |
greenColor.dispose(); | |
blueColor.dispose(); | |
fixedWidthFont.dispose(); | |
crossCursor.dispose(); | |
imageCanvasGC.dispose(); | |
} | |
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(bundle.getString("Background")); | |
backgroundCombo = new Combo(group, SWT.DROP_DOWN | SWT.READ_ONLY); | |
backgroundCombo.setItems(new String[] { | |
bundle.getString("None"), | |
bundle.getString("White"), | |
bundle.getString("Black"), | |
bundle.getString("Red"), | |
bundle.getString("Green"), | |
bundle.getString("Blue")}); | |
backgroundCombo.select(backgroundCombo.indexOf(bundle.getString("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(bundle.getString("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(bundle.getString("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(bundle.getString("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(bundle.getString("Display")); | |
incrementalCheck = new Button(group, SWT.CHECK); | |
incrementalCheck.setText(bundle.getString("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(bundle.getString("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(bundle.getString("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(bundle.getString("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(bundle.getString("Animation")); | |
// Push button to display the previous image in a multi-image file. | |
previousButton = new Button(group, SWT.PUSH); | |
previousButton.setText(bundle.getString("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(bundle.getString("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(bundle.getString("Animate")); | |
animateButton.setEnabled(false); | |
animateButton.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent event) { | |
animate(); | |
} | |
}); | |
// Separate the control widgets from the rest of the widgets. | |
// Label separator = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL); | |
// gridData = new GridData(); | |
// gridData.horizontalSpan = 2; | |
// gridData.horizontalAlignment = GridData.FILL; | |
// separator.setLayoutData(gridData); | |
// Label to show the image file type. | |
typeLabel = new Label(shell, SWT.NULL); | |
typeLabel.setText(bundle.getString("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 = 14; | |
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(bundle.getString("Size_initial")); | |
sizeLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); | |
// Label to show the image depth. | |
depthLabel = new Label(shell, SWT.NULL); | |
depthLabel.setText(bundle.getString("Depth_initial")); | |
depthLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); | |
// Label to show the transparent pixel. | |
transparentPixelLabel = new Label(shell, SWT.NULL); | |
transparentPixelLabel.setText(bundle.getString("Transparent_pixel_initial")); | |
transparentPixelLabel.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(bundle.getString("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(bundle.getString("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(bundle.getString("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(bundle.getString("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(bundle.getString("Delay_initial")); | |
delayTimeLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); | |
// Label to show the background pixel. | |
repeatCountLabel = new Label(shell, SWT.NULL); | |
repeatCountLabel.setText(bundle.getString("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(bundle.getString("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(bundle.getString("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 sash = new Sash(shell, SWT.HORIZONTAL); | |
gridData = new GridData(); | |
gridData.horizontalSpan = 2; | |
gridData.horizontalAlignment = GridData.FILL; | |
sash.setLayoutData(gridData); | |
// Label to show data-specific fields. | |
dataLabel = new Label(shell, SWT.NULL); | |
dataLabel.setText(bundle.getString("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 Text(shell, SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.V_SCROLL | SWT.H_SCROLL); | |
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(bundle.getString("File")); | |
Menu fileMenu = new Menu(shell, SWT.DROP_DOWN); | |
item.setMenu(fileMenu); | |
// File -> Open... | |
item = new MenuItem(fileMenu, SWT.NULL); | |
item.setText(bundle.getString("Open")); | |
item.setAccelerator(SWT.CTRL + 'O'); | |
item.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent event) { | |
menuOpen(); | |
} | |
}); | |
// File -> Reopen | |
item = new MenuItem(fileMenu, SWT.NULL); | |
item.setText(bundle.getString("Reopen")); | |
item.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent event) { | |
menuReopen(); | |
} | |
}); | |
new MenuItem(fileMenu, SWT.SEPARATOR); | |
// File -> Save | |
item = new MenuItem(fileMenu, SWT.NULL); | |
item.setText(bundle.getString("Save")); | |
item.setAccelerator(SWT.CTRL + 'S'); | |
item.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent event) { | |
menuSave(); | |
} | |
}); | |
// File -> Save As... | |
item = new MenuItem(fileMenu, SWT.NULL); | |
item.setText(bundle.getString("Save_as")); | |
item.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent event) { | |
menuSaveAs(); | |
} | |
}); | |
// File -> Save Mask As... | |
item = new MenuItem(fileMenu, SWT.NULL); | |
item.setText(bundle.getString("Save_mask_as")); | |
item.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent event) { | |
menuSaveMaskAs(); | |
} | |
}); | |
new MenuItem(fileMenu, SWT.SEPARATOR); | |
// File -> Exit | |
item = new MenuItem(fileMenu, SWT.NULL); | |
item.setText(bundle.getString("Exit")); | |
item.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent event) { | |
animate = false; // stop any animation in progress | |
shell.close(); | |
} | |
}); | |
} | |
void createAlphaMenu(Menu menuBar) { | |
// Alpha menu | |
MenuItem item = new MenuItem(menuBar, SWT.CASCADE); | |
item.setText(bundle.getString("Alpha")); | |
Menu alphaMenu = new Menu(shell, SWT.DROP_DOWN); | |
item.setMenu(alphaMenu); | |
// Alpha -> K | |
item = new MenuItem(alphaMenu, SWT.NULL); | |
item.setText("K"); | |
item.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent event) { | |
menuComposeAlpha(ALPHA_CONSTANT); | |
} | |
}); | |
// Alpha -> (K + x) % 256 | |
item = new MenuItem(alphaMenu, SWT.NULL); | |
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.NULL); | |
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 menuOpen() { | |
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[] { bundle.getString("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. | |
imageDataArray = loader.load(filename); | |
if (imageDataArray.length > 0) { | |
// Cache the 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(bundle.getString("Loading_lc"), filename, 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(null, 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) { | |
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(bundle.getString("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); | |
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(bundle.getString("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(bundle.getString("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(bundle.getString("Analyzer_on"), filename)); | |
typeLabel.setText(createMsg(bundle.getString("Type_string"), fileTypeString(filetype))); | |
} catch (SWTException e) { | |
showErrorDialog(bundle.getString("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); | |
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(bundle.getString("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(bundle.getString("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(bundle.getString("Saving_lc"), filename, e); | |
} finally { | |
shell.setCursor(null); | |
imageCanvas.setCursor(crossCursor); | |
waitCursor.dispose(); | |
} | |
} | |
void menuReopen() { | |
if (fileName == 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(); | |
ImageData[] newImageData = loader.load(fileName); | |
imageDataIndex = 0; | |
displayImage(newImageData[imageDataIndex]); | |
} catch (SWTException e) { | |
showErrorDialog(bundle.getString("Reloading_lc"), fileName, e); | |
} finally { | |
shell.setCursor(null); | |
imageCanvas.setCursor(crossCursor); | |
waitCursor.dispose(); | |
} | |
} | |
void changeBackground() { | |
String background = backgroundCombo.getText(); | |
if (background.equals(bundle.getString("White"))) { | |
imageCanvas.setBackground(whiteColor); | |
} else if (background.equals(bundle.getString("Black"))) { | |
imageCanvas.setBackground(blackColor); | |
} else if (background.equals(bundle.getString("Red"))) { | |
imageCanvas.setBackground(redColor); | |
} else if (background.equals(bundle.getString("Green"))) { | |
imageCanvas.setBackground(greenColor); | |
} else if (background.equals(bundle.getString("Blue"))) { | |
imageCanvas.setBackground(blueColor); | |
} else { | |
imageCanvas.setBackground(null); | |
} | |
// Fill the image canvas with the background color. | |
Rectangle clientArea = imageCanvas.getClientArea(); | |
imageCanvasGC.fillRectangle( | |
clientArea.x, | |
clientArea.y, | |
clientArea.width, | |
clientArea.height); | |
if (image != null) { | |
imageCanvas.redraw(); | |
} | |
} | |
/* | |
* 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.getCaretPosition(); | |
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(bundle.getString("Color_at_trans"), args)); | |
} else { | |
statusLabel.setText(createMsg(bundle.getString("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(bundle.getString("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(bundle.getString("Creating_image"), | |
new Integer(imageDataIndex+1)), | |
fileName, 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(null, 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(null, 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(bundle.getString("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(bundle.getString("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(null, newImageData); | |
imageData = newImageData; | |
} catch (SWTException e) { | |
showErrorDialog(bundle.getString("Creating_from") + " ", fileName, e); | |
image = null; | |
return; | |
} | |
// Update the widgets with the new image info. | |
String string = createMsg(bundle.getString("Analyzer_on"), fileName); | |
shell.setText(string); | |
if(imageDataArray.length > 1){ | |
string = createMsg(bundle.getString("Type_index"), | |
new Object[] {fileTypeString(imageData.type), | |
new Integer(imageDataIndex + 1), | |
new Integer(imageDataArray.length)}); | |
} else { | |
string = createMsg(bundle.getString("Type_string"), fileTypeString(imageData.type)); | |
} | |
typeLabel.setText(string); | |
string = createMsg(bundle.getString("Size_value"), | |
new Object[] {new Integer(imageData.width), | |
new Integer(imageData.height)}); | |
sizeLabel.setText(string); | |
string = createMsg(bundle.getString("Depth_value"), new Integer(imageData.depth)); | |
depthLabel.setText(string); | |
string = createMsg(bundle.getString("Transparent_pixel_value"), pixelInfo(imageData.transparentPixel)); | |
transparentPixelLabel.setText(string); | |
string = createMsg(bundle.getString("Animation_size_value"), | |
new Object[] {new Integer(loader.logicalScreenWidth), | |
new Integer(loader.logicalScreenHeight)}); | |
screenSizeLabel.setText(string); | |
string = createMsg(bundle.getString("Background_pixel_value"), pixelInfo(loader.backgroundPixel)); | |
backgroundPixelLabel.setText(string); | |
string = createMsg(bundle.getString("Image_location_value"), | |
new Object[] {new Integer(imageData.x), new Integer(imageData.y)}); | |
locationLabel.setText(string); | |
string = createMsg(bundle.getString("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(bundle.getString("Delay_value"), | |
new Object[] {new Integer(delay), new Integer(delayUsed)}); | |
} else { | |
string = createMsg(bundle.getString("Delay_used"), new Integer(delay)); | |
} | |
delayTimeLabel.setText(string); | |
if(loader.repeatCount == 0){ | |
string = createMsg( bundle.getString("Repeats_forever"), new Integer(loader.repeatCount)); | |
} else { | |
string = createMsg(bundle.getString("Repeats_value"), new Integer(loader.repeatCount)); | |
} | |
repeatCountLabel.setText(string); | |
if(imageData.palette.isDirect){ | |
string = bundle.getString("Palette_direct"); | |
} else { | |
string = createMsg(bundle.getString("Palette_value"), new Integer(imageData.palette.getRGBs().length)); | |
} | |
paletteLabel.setText(string); | |
string = createMsg(bundle.getString("Pixel_data_value"), | |
new Object[] {new Integer(imageData.bytesPerLine), | |
new Integer(imageData.scanlinePad), | |
depthInfo(imageData.depth)}); | |
dataLabel.setText(string); | |
dataText.setText(dataHexDump(dataText.getLineDelimiter())); | |
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); | |
} | |
} | |
return new String(dump); | |
} | |
/* | |
* Open an error dialog displaying the specified information. | |
*/ | |
void showErrorDialog(String operation, String filename, SWTException e) { | |
MessageBox box = new MessageBox(shell, SWT.ICON_ERROR); | |
String message = createMsg(bundle.getString("Error"), new String[] {operation, filename}); | |
String errorMessage = e.getMessage(); | |
if (e.throwable != null) { | |
errorMessage += ":\n" + e.throwable.toString(); | |
} | |
box.setMessage(message + errorMessage); | |
box.open(); | |
} | |
/* | |
* 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(bundle.getString("Multi_pixels"), | |
new Object[] {new Integer(8), " [01234567]"}); | |
break; | |
case 2: | |
args[1] = createMsg(bundle.getString("Multi_pixels"), | |
new Object[] {new Integer(4), "[00112233]"}); | |
break; | |
case 4: | |
args[1] = createMsg(bundle.getString("Multi_pixels"), | |
new Object[] {new Integer(2), "[00001111]"}); | |
break; | |
case 8: | |
args[1] = bundle.getString("One_byte"); | |
break; | |
case 16: | |
args[1] = createMsg(bundle.getString("Multi_bytes"), new Integer(2)); | |
break; | |
case 24: | |
args[1] = createMsg(bundle.getString("Multi_bytes"), new Integer(3)); | |
break; | |
case 32: | |
args[1] = createMsg(bundle.getString("Multi_bytes"), new Integer(4)); | |
break; | |
default: | |
args[1] = bundle.getString("Unsupported_lc"); | |
} | |
return createMsg(bundle.getString("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 + " (" + bundle.getString("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 bundle.getString("None_lc"); | |
case SWT.DM_FILL_BACKGROUND: return bundle.getString("Background_lc"); | |
case SWT.DM_FILL_PREVIOUS: return bundle.getString("Previous_lc"); | |
} | |
return bundle.getString("Unspecified_lc"); | |
} | |
/* | |
* Return a String describing the specified image file type. | |
*/ | |
static String fileTypeString(int filetype) { | |
if (filetype == SWT.IMAGE_BMP) | |
return "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 bundle.getString("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. | |
*/ | |
static int determineFileType(String filename) { | |
String ext = filename.substring(filename.lastIndexOf('.') + 1); | |
if (ext.equalsIgnoreCase("bmp")) | |
return SWT.IMAGE_BMP; | |
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}); | |
} | |
} | |