0da1c5dcca
First commit of the 4.5 version (latest version available)
566 lines
27 KiB
Java
566 lines
27 KiB
Java
package mars.tools;
|
|
import javax.swing.*;
|
|
import javax.swing.border.*;
|
|
import javax.swing.event.*;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.util.*;
|
|
import mars.tools.*;
|
|
import mars.mips.hardware.*;
|
|
|
|
/*
|
|
Copyright (c) 2010-2011, Pete Sanderson and Kenneth Vollmar
|
|
|
|
Developed by Pete Sanderson (psanderson@otterbein.edu)
|
|
and Kenneth Vollmar (kenvollmar@missouristate.edu)
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject
|
|
to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
|
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
(MIT license, http://www.opensource.org/licenses/mit-license.html)
|
|
*/
|
|
|
|
/**
|
|
* Bitmapp display simulator. It can be run either as a stand-alone Java application having
|
|
* access to the mars package, or through MARS as an item in its Tools menu. It makes
|
|
* maximum use of methods inherited from its abstract superclass AbstractMarsToolAndApplication.
|
|
* Pete Sanderson, verison 1.0, 23 December 2010.
|
|
*/
|
|
public class BitmapDisplay extends AbstractMarsToolAndApplication {
|
|
|
|
private static String version = "Version 1.0";
|
|
private static String heading = "Bitmap Display";
|
|
|
|
// Major GUI components
|
|
private JComboBox visualizationUnitPixelWidthSelector, visualizationUnitPixelHeightSelector,
|
|
visualizationPixelWidthSelector, visualizationPixelHeightSelector, displayBaseAddressSelector;
|
|
private Graphics drawingArea;
|
|
private JPanel canvas;
|
|
private JPanel results;
|
|
|
|
// Some GUI settings
|
|
private EmptyBorder emptyBorder = new EmptyBorder(4,4,4,4);
|
|
private Font countFonts = new Font("Times", Font.BOLD,12);
|
|
private Color backgroundColor = Color.WHITE;
|
|
|
|
// Values for Combo Boxes
|
|
|
|
private final String[] visualizationUnitPixelWidthChoices = {"1","2","4","8","16","32"};
|
|
private final int defaultVisualizationUnitPixelWidthIndex = 0;
|
|
private final String[] visualizationUnitPixelHeightChoices = {"1","2","4","8","16","32"};
|
|
private final int defaultVisualizationUnitPixelHeightIndex = 0;
|
|
private final String[] displayAreaPixelWidthChoices = {"64","128","256","512","1024"};
|
|
private final int defaultDisplayWidthIndex = 3;
|
|
private final String[] displayAreaPixelHeightChoices = {"64","128","256","512","1024"};
|
|
private final int defaultDisplayHeightIndex = 2;
|
|
|
|
// Values for display canvas. Note their initialization uses the identifiers just above.
|
|
|
|
private int unitPixelWidth = Integer.parseInt(visualizationUnitPixelWidthChoices[defaultVisualizationUnitPixelWidthIndex]);
|
|
private int unitPixelHeight = Integer.parseInt(visualizationUnitPixelHeightChoices[defaultVisualizationUnitPixelHeightIndex]);
|
|
private int displayAreaWidthInPixels = Integer.parseInt(displayAreaPixelWidthChoices[defaultDisplayWidthIndex]);
|
|
private int displayAreaHeightInPixels = Integer.parseInt(displayAreaPixelHeightChoices[defaultDisplayHeightIndex]);
|
|
|
|
|
|
// The next four are initialized dynamically in initializeDisplayBaseChoices()
|
|
private String[] displayBaseAddressChoices;
|
|
private int[] displayBaseAddresses;
|
|
private int defaultBaseAddressIndex;
|
|
private int baseAddress;
|
|
|
|
private Grid theGrid;
|
|
|
|
/**
|
|
* Simple constructor, likely used to run a stand-alone bitmap display tool.
|
|
* @param title String containing title for title bar
|
|
* @param heading String containing text for heading shown in upper part of window.
|
|
*/
|
|
public BitmapDisplay(String title, String heading) {
|
|
super(title,heading);
|
|
}
|
|
|
|
/**
|
|
* Simple constructor, likely used by the MARS Tools menu mechanism
|
|
*/
|
|
public BitmapDisplay() {
|
|
super ("Bitmap Display, "+version, heading);
|
|
}
|
|
|
|
|
|
/**
|
|
* Main provided for pure stand-alone use. Recommended stand-alone use is to write a
|
|
* driver program that instantiates a Bitmap object then invokes its go() method.
|
|
* "stand-alone" means it is not invoked from the MARS Tools menu. "Pure" means there
|
|
* is no driver program to invoke the application.
|
|
*/
|
|
public static void main(String[] args) {
|
|
new BitmapDisplay("Bitmap Display stand-alone, "+version,heading).go();
|
|
}
|
|
|
|
|
|
/**
|
|
* Required MarsTool method to return Tool name.
|
|
* @return Tool name. MARS will display this in menu item.
|
|
*/
|
|
public String getName() {
|
|
return "Bitmap Display";
|
|
}
|
|
|
|
|
|
/**
|
|
* Override the inherited method, which registers us as an Observer over the static data segment
|
|
* (starting address 0x10010000) only. This version will register us as observer over the
|
|
* the memory range as selected by the base address combo box and capacity of the visualization display
|
|
* (number of visualization elements times the number of memory words each one represents).
|
|
* It does so by calling the inherited 2-parameter overload of this method.
|
|
* If you use the inherited GUI buttons, this
|
|
* method is invoked when you click "Connect" button on MarsTool or the
|
|
* "Assemble and Run" button on a Mars-based app.
|
|
*/
|
|
protected void addAsObserver() {
|
|
int highAddress = baseAddress+theGrid.getRows()*theGrid.getColumns()*Memory.WORD_LENGTH_BYTES;
|
|
// Special case: baseAddress<0 means we're in kernel memory (0x80000000 and up) and most likely
|
|
// in memory map address space (0xffff0000 and up). In this case, we need to make sure the high address
|
|
// does not drop off the high end of 32 bit address space. Highest allowable word address is 0xfffffffc,
|
|
// which is interpreted in Java int as -4.
|
|
if (baseAddress < 0 && highAddress > -4) {
|
|
highAddress = -4;
|
|
}
|
|
addAsObserver(baseAddress, highAddress);
|
|
}
|
|
|
|
|
|
/**
|
|
* Method that constructs the main display area. It is organized vertically
|
|
* into two major components: the display configuration which an be modified
|
|
* using combo boxes, and the visualization display which is updated as the
|
|
* attached MIPS program executes.
|
|
* @return the GUI component containing these two areas
|
|
*/
|
|
protected JComponent buildMainDisplayArea() {
|
|
results = new JPanel();
|
|
results.add(buildOrganizationArea());
|
|
results.add(buildVisualizationArea());
|
|
return results;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Rest of the protected methods. These override do-nothing methods inherited from
|
|
// the abstract superclass.
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Update display when connected MIPS program accesses (data) memory.
|
|
* @param memory the attached memory
|
|
* @param accessNotice information provided by memory in MemoryAccessNotice object
|
|
*/
|
|
protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) {
|
|
if (accessNotice.getAccessType() == AccessNotice.WRITE) {
|
|
updateColorForAddress((MemoryAccessNotice)accessNotice);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize all JComboBox choice structures not already initialized at declaration.
|
|
* Overrides inherited method that does nothing.
|
|
*/
|
|
protected void initializePreGUI() {
|
|
initializeDisplayBaseChoices();
|
|
// NOTE: Can't call "createNewGrid()" here because it uses settings from
|
|
// several combo boxes that have not been created yet. But a default grid
|
|
// needs to be allocated for initial canvas display.
|
|
theGrid = new Grid(displayAreaHeightInPixels/unitPixelHeight,
|
|
displayAreaWidthInPixels/unitPixelWidth);
|
|
}
|
|
|
|
|
|
/**
|
|
* The only post-GUI initialization is to create the initial Grid object based on the default settings
|
|
* of the various combo boxes. Overrides inherited method that does nothing.
|
|
*/
|
|
|
|
protected void initializePostGUI() {
|
|
theGrid = createNewGrid();
|
|
updateBaseAddress();
|
|
}
|
|
|
|
|
|
/**
|
|
* Method to reset counters and display when the Reset button selected.
|
|
* Overrides inherited method that does nothing.
|
|
*/
|
|
protected void reset() {
|
|
resetCounts();
|
|
updateDisplay();
|
|
}
|
|
|
|
/**
|
|
* Updates display immediately after each update (AccessNotice) is processed, after
|
|
* display configuration changes as needed, and after each execution step when Mars
|
|
* is running in timed mode. Overrides inherited method that does nothing.
|
|
*/
|
|
protected void updateDisplay() {
|
|
canvas.repaint();
|
|
}
|
|
|
|
|
|
/**
|
|
* Overrides default method, to provide a Help button for this tool/app.
|
|
*/
|
|
protected JComponent getHelpComponent() {
|
|
final String helpContent =
|
|
"Use this program to simulate a basic bitmap display where\n"+
|
|
"each memory word in a specified address space corresponds to\n"+
|
|
"one display pixel in row-major order starting at the upper left\n"+
|
|
"corner of the display. This tool may be run either from the\n"+
|
|
"MARS Tools menu or as a stand-alone application.\n"+
|
|
"\n"+
|
|
"You can easily learn to use this small program by playing with\n"+
|
|
"it! Each rectangular unit on the display represents one memory\n"+
|
|
"word in a contiguous address space starting with the specified\n"+
|
|
"base address. The value stored in that word will be interpreted\n"+
|
|
"as a 24-bit RGB color value with the red component in bits 16-23,\n"+
|
|
"the green component in bits 8-15, and the blue component in bits 0-7.\n"+
|
|
"Each time a memory word within the display address space is written\n"+
|
|
"by the MIPS program, its position in the display will be rendered\n"+
|
|
"in the color that its value represents.\n"+
|
|
"\n"+
|
|
"Version 1.0 is very basic and was constructed from the Memory\n"+
|
|
"Reference Visualization tool's code. Feel free to improve it and\n"+
|
|
"send me your code for consideration in the next MARS release.\n"+
|
|
"\n"+
|
|
"Contact Pete Sanderson at psanderson@otterbein.edu with\n"+
|
|
"questions or comments.\n";
|
|
JButton help = new JButton("Help");
|
|
help.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
JOptionPane.showMessageDialog(theWindow, helpContent);
|
|
}
|
|
});
|
|
return help;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Private methods defined to support the above.
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// UI components and layout for left half of GUI, where settings are specified.
|
|
private JComponent buildOrganizationArea() {
|
|
JPanel organization = new JPanel(new GridLayout(8,1));
|
|
|
|
visualizationUnitPixelWidthSelector = new JComboBox(visualizationUnitPixelWidthChoices);
|
|
visualizationUnitPixelWidthSelector.setEditable(false);
|
|
visualizationUnitPixelWidthSelector.setBackground(backgroundColor);
|
|
visualizationUnitPixelWidthSelector.setSelectedIndex(defaultVisualizationUnitPixelWidthIndex);
|
|
visualizationUnitPixelWidthSelector.setToolTipText("Width in pixels of rectangle representing memory word");
|
|
visualizationUnitPixelWidthSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
unitPixelWidth = getIntComboBoxSelection(visualizationUnitPixelWidthSelector);
|
|
theGrid = createNewGrid();
|
|
updateDisplay();
|
|
}
|
|
});
|
|
visualizationUnitPixelHeightSelector = new JComboBox(visualizationUnitPixelHeightChoices);
|
|
visualizationUnitPixelHeightSelector.setEditable(false);
|
|
visualizationUnitPixelHeightSelector.setBackground(backgroundColor);
|
|
visualizationUnitPixelHeightSelector.setSelectedIndex(defaultVisualizationUnitPixelHeightIndex);
|
|
visualizationUnitPixelHeightSelector.setToolTipText("Height in pixels of rectangle representing memory word");
|
|
visualizationUnitPixelHeightSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
unitPixelHeight = getIntComboBoxSelection(visualizationUnitPixelHeightSelector);
|
|
theGrid = createNewGrid();
|
|
updateDisplay();
|
|
}
|
|
});
|
|
visualizationPixelWidthSelector = new JComboBox(displayAreaPixelWidthChoices);
|
|
visualizationPixelWidthSelector.setEditable(false);
|
|
visualizationPixelWidthSelector.setBackground(backgroundColor);
|
|
visualizationPixelWidthSelector.setSelectedIndex(defaultDisplayWidthIndex);
|
|
visualizationPixelWidthSelector.setToolTipText("Total width in pixels of display area");
|
|
visualizationPixelWidthSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
displayAreaWidthInPixels = getIntComboBoxSelection(visualizationPixelWidthSelector);
|
|
canvas.setPreferredSize(getDisplayAreaDimension());
|
|
canvas.setSize(getDisplayAreaDimension());
|
|
theGrid = createNewGrid();
|
|
updateDisplay();
|
|
}
|
|
});
|
|
visualizationPixelHeightSelector = new JComboBox(displayAreaPixelHeightChoices);
|
|
visualizationPixelHeightSelector.setEditable(false);
|
|
visualizationPixelHeightSelector.setBackground(backgroundColor);
|
|
visualizationPixelHeightSelector.setSelectedIndex(defaultDisplayHeightIndex);
|
|
visualizationPixelHeightSelector.setToolTipText("Total height in pixels of display area");
|
|
visualizationPixelHeightSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
displayAreaHeightInPixels = getIntComboBoxSelection(visualizationPixelHeightSelector);
|
|
canvas.setPreferredSize(getDisplayAreaDimension());
|
|
canvas.setSize(getDisplayAreaDimension());
|
|
theGrid = createNewGrid();
|
|
updateDisplay();
|
|
}
|
|
});
|
|
displayBaseAddressSelector = new JComboBox(displayBaseAddressChoices);
|
|
displayBaseAddressSelector.setEditable(false);
|
|
displayBaseAddressSelector.setBackground(backgroundColor);
|
|
displayBaseAddressSelector.setSelectedIndex(defaultBaseAddressIndex);
|
|
displayBaseAddressSelector.setToolTipText("Base address for display area (upper left corner)");
|
|
displayBaseAddressSelector.addActionListener(
|
|
new ActionListener() {
|
|
public void actionPerformed(ActionEvent e) {
|
|
// This may also affect what address range we should be registered as an Observer
|
|
// for. The default (inherited) address range is the MIPS static data segment
|
|
// starting at 0x10010000. To change this requires override of
|
|
// AbstractMarsToolAndApplication.addAsObserver(). The no-argument version of
|
|
// that method is called automatically when "Connect" button is clicked for MarsTool
|
|
// and when "Assemble and Run" button is clicked for Mars application.
|
|
updateBaseAddress();
|
|
// If display base address is changed while connected to MIPS (this can only occur
|
|
// when being used as a MarsTool), we have to delete ourselves as an observer and re-register.
|
|
if (connectButton != null && connectButton.isConnected()) {
|
|
deleteAsObserver();
|
|
addAsObserver();
|
|
}
|
|
theGrid = createNewGrid();
|
|
updateDisplay();
|
|
}
|
|
});
|
|
|
|
// ALL COMPONENTS FOR "ORGANIZATION" SECTION
|
|
|
|
JPanel unitWidthInPixelsRow = getPanelWithBorderLayout();
|
|
unitWidthInPixelsRow.setBorder(emptyBorder);
|
|
unitWidthInPixelsRow.add(new JLabel("Unit Width in Pixels "),BorderLayout.WEST);
|
|
unitWidthInPixelsRow.add(visualizationUnitPixelWidthSelector, BorderLayout.EAST);
|
|
|
|
JPanel unitHeightInPixelsRow = getPanelWithBorderLayout();
|
|
unitHeightInPixelsRow.setBorder(emptyBorder);
|
|
unitHeightInPixelsRow.add(new JLabel("Unit Height in Pixels "),BorderLayout.WEST);
|
|
unitHeightInPixelsRow.add(visualizationUnitPixelHeightSelector,BorderLayout.EAST);
|
|
|
|
JPanel widthInPixelsRow = getPanelWithBorderLayout();
|
|
widthInPixelsRow.setBorder(emptyBorder);
|
|
widthInPixelsRow.add(new JLabel("Display Width in Pixels "),BorderLayout.WEST);
|
|
widthInPixelsRow.add(visualizationPixelWidthSelector, BorderLayout.EAST);
|
|
|
|
JPanel heightInPixelsRow = getPanelWithBorderLayout();
|
|
heightInPixelsRow.setBorder(emptyBorder);
|
|
heightInPixelsRow.add(new JLabel("Display Height in Pixels "),BorderLayout.WEST);
|
|
heightInPixelsRow.add(visualizationPixelHeightSelector,BorderLayout.EAST);
|
|
|
|
JPanel baseAddressRow = getPanelWithBorderLayout();
|
|
baseAddressRow.setBorder(emptyBorder);
|
|
baseAddressRow.add(new JLabel("Base address for display "),BorderLayout.WEST);
|
|
baseAddressRow.add(displayBaseAddressSelector,BorderLayout.EAST);
|
|
|
|
|
|
// Lay 'em out in the grid...
|
|
organization.add(unitWidthInPixelsRow);
|
|
organization.add(unitHeightInPixelsRow);
|
|
organization.add(widthInPixelsRow);
|
|
organization.add(heightInPixelsRow);
|
|
organization.add(baseAddressRow);
|
|
return organization;
|
|
}
|
|
|
|
// UI components and layout for right half of GUI, the visualization display area.
|
|
private JComponent buildVisualizationArea() {
|
|
canvas = new GraphicsPanel();
|
|
canvas.setPreferredSize(getDisplayAreaDimension());
|
|
canvas.setToolTipText("Bitmap display area");
|
|
return canvas;
|
|
}
|
|
|
|
// For greatest flexibility, initialize the display base choices directly from
|
|
// the constants defined in the Memory class. This method called prior to
|
|
// building the GUI. Here are current values from Memory.java:
|
|
//dataSegmentBaseAddress=0x10000000, globalPointer=0x10008000
|
|
//dataBaseAddress=0x10010000, heapBaseAddress=0x10040000, memoryMapBaseAddress=0xffff0000
|
|
private void initializeDisplayBaseChoices() {
|
|
int[] displayBaseAddressArray = {Memory.dataSegmentBaseAddress, Memory.globalPointer, Memory.dataBaseAddress,
|
|
Memory.heapBaseAddress, Memory.memoryMapBaseAddress };
|
|
// Must agree with above in number and order...
|
|
String[] descriptions = { " (global data)", " ($gp)", " (static data)", " (heap)", " (memory map)" };
|
|
displayBaseAddresses = displayBaseAddressArray;
|
|
displayBaseAddressChoices = new String[displayBaseAddressArray.length];
|
|
for (int i=0; i<displayBaseAddressChoices.length; i++) {
|
|
displayBaseAddressChoices[i] = mars.util.Binary.intToHexString(displayBaseAddressArray[i])+descriptions[i];
|
|
}
|
|
defaultBaseAddressIndex = 2; // default to 0x10010000 (static data)
|
|
baseAddress = displayBaseAddressArray[defaultBaseAddressIndex];
|
|
}
|
|
|
|
// update based on combo box selection (currently not editable but that may change).
|
|
private void updateBaseAddress() {
|
|
baseAddress = displayBaseAddresses[displayBaseAddressSelector.getSelectedIndex()];
|
|
/* If you want to extend this app to allow user to edit combo box, you can always
|
|
parse the getSelectedItem() value, because the pre-defined items are all formatted
|
|
such that the first 10 characters contain the integer's hex value. And if the
|
|
value is user-entered, the numeric part cannot exceed 10 characters for a 32-bit
|
|
address anyway. So if the value is > 10 characters long, slice off the first
|
|
10 and apply Integer.parseInt() to it to get custom base address.
|
|
*/
|
|
}
|
|
|
|
// Returns Dimension object with current width and height of display area as determined
|
|
// by current settings of respective combo boxes.
|
|
private Dimension getDisplayAreaDimension() {
|
|
return new Dimension(displayAreaWidthInPixels, displayAreaHeightInPixels);
|
|
}
|
|
|
|
// reset all counters in the Grid.
|
|
private void resetCounts() {
|
|
theGrid.reset();
|
|
}
|
|
|
|
// Will return int equivalent of specified combo box's current selection.
|
|
// The selection must be a String that parses to an int.
|
|
private int getIntComboBoxSelection(JComboBox comboBox) {
|
|
try {
|
|
return Integer.parseInt((String)comboBox.getSelectedItem());
|
|
}
|
|
catch (NumberFormatException nfe) {
|
|
// Can occur only if initialization list contains badly formatted numbers. This
|
|
// is a developer's error, not a user error, and better be caught before release.
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Use this for consistent results.
|
|
private JPanel getPanelWithBorderLayout() {
|
|
return new JPanel(new BorderLayout(2,2));
|
|
}
|
|
|
|
// Method to determine grid dimensions based on current control settings.
|
|
// Each grid element corresponds to one visualization unit.
|
|
private Grid createNewGrid() {
|
|
int rows = displayAreaHeightInPixels/unitPixelHeight;
|
|
int columns = displayAreaWidthInPixels/unitPixelWidth;
|
|
return new Grid(rows,columns);
|
|
}
|
|
|
|
// Given memory address, update color for the corresponding grid element.
|
|
private void updateColorForAddress(MemoryAccessNotice notice) {
|
|
int address = notice.getAddress();
|
|
int value = notice.getValue();
|
|
int offset = (address - baseAddress)/Memory.WORD_LENGTH_BYTES;
|
|
try {
|
|
theGrid.setElement(offset / theGrid.getColumns(), offset % theGrid.getColumns(), value);
|
|
}
|
|
catch (IndexOutOfBoundsException e) {
|
|
// If address is out of range for display, do nothing.
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// Specialized inner classes for modeling and animation.
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Class that represents the panel for visualizing and animating memory reference
|
|
// patterns.
|
|
private class GraphicsPanel extends JPanel {
|
|
|
|
// override default paint method to assure display updated correctly every time
|
|
// the panel is repainted.
|
|
public void paint(Graphics g) {
|
|
paintGrid(g, theGrid);
|
|
}
|
|
|
|
// Paint the color codes.
|
|
private void paintGrid(Graphics g, Grid grid) {
|
|
int upperLeftX = 0, upperLeftY = 0;
|
|
for (int i=0; i<grid.getRows(); i++) {
|
|
for (int j=0; j<grid.getColumns(); j++) {
|
|
g.setColor(grid.getElementFast(i,j));
|
|
g.fillRect(upperLeftX, upperLeftY, unitPixelWidth, unitPixelHeight);
|
|
upperLeftX += unitPixelWidth; // faster than multiplying
|
|
}
|
|
// get ready for next row...
|
|
upperLeftX = 0;
|
|
upperLeftY += unitPixelHeight; // faster than multiplying
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Represents grid of colors
|
|
private class Grid {
|
|
|
|
Color[][] grid;
|
|
int rows, columns;
|
|
|
|
private Grid(int rows, int columns) {
|
|
grid = new Color[rows][columns];
|
|
this.rows = rows;
|
|
this.columns = columns;
|
|
reset();
|
|
}
|
|
|
|
private int getRows() {
|
|
return rows;
|
|
}
|
|
|
|
private int getColumns() {
|
|
return columns;
|
|
}
|
|
|
|
// Returns value in given grid element; null if row or column is out of range.
|
|
private Color getElement(int row, int column) {
|
|
return (row>=0 && row<=rows && column>=0 && column<=columns) ? grid[row][column] : null;
|
|
}
|
|
|
|
// Returns value in given grid element without doing any row/column index checking.
|
|
// Is faster than getElement but will throw array index out of bounds exception if
|
|
// parameter values are outside the bounds of the grid.
|
|
private Color getElementFast(int row, int column) {
|
|
return grid[row][column];
|
|
}
|
|
|
|
// Set the grid element.
|
|
private void setElement(int row, int column, int color) {
|
|
grid[row][column] = new Color(color);
|
|
}
|
|
|
|
// Set the grid element.
|
|
private void setElement(int row, int column, Color color) {
|
|
grid[row][column] = color;
|
|
}
|
|
|
|
// Just set all grid elements to black.
|
|
private void reset() {
|
|
for (int i=0; i<rows; i++) {
|
|
for (int j=0; j<columns; j++) {
|
|
grid[i][j] = Color.BLACK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} |