/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.display; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.geom.AffineTransform; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.MemoryImageSource; import org.opensourcephysics.display.DisplayRes; import org.opensourcephysics.display.DrawingPanel; import org.opensourcephysics.display.OSPRuntime; /** * A ByteImage contains an array of bytes int[row][col] * where each integer represents an image pixel. The row index determines the y-location * of the pixel and the col index determines the x-location in the drawing panel. * * @author Wolfgang Christian * @created March 3, 2012 * @version 1.0 */ public class ByteImage implements Measurable { byte[] imagePixels; // array that gets mapped onto the image MemoryImageSource imageSource; // object that converts the array to an image Image image; // image to be rendered in drawing panel int nrow, ncol; // number of rows and column in array double xmin, xmax, ymin, ymax; // drawing scale boolean visible = true; boolean dirtyImage=true; // true if array elements have changed /** * Gets a two-color ByteImage with 0 -> red and 1 -> blue. */ static public ByteImage getBinaryImage(byte[][] data){ ColorModel colorModel = new IndexColorModel(1, 2, new byte[] {(byte) 255, (byte) 0}, new byte[] {(byte) 0, (byte) 0}, new byte[] {(byte) 0, (byte) 255}); return new ByteImage(colorModel, data); } // Gets an ByteImage with the given color palette and data. static public ByteImage getColorImage(Color[] colors, byte[][] data){ int n=colors.length; byte [] reds = new byte[n]; byte [] greens = new byte[n]; byte [] blues = new byte[n]; for(int i = 0; i<n; i++) { reds[i] = (byte) (colors[i].getRed()); greens[i] = (byte) (colors[i].getGreen()); blues[i] = (byte) (colors[i].getBlue()); } ColorModel colorModel = new IndexColorModel(8, n, reds, greens, blues); return new ByteImage(colorModel, data); } /** * Constructs ByteImage with the given data. * * @param data */ public ByteImage(byte[][] data) { byte [] reds = new byte[256]; byte [] greens = new byte[256]; byte [] blues = new byte[256]; for(int i = 0; i<256; i++) { double x = (i<128) ? (i-100)/255.0 : -1; double val = Math.exp(-x*x*8); reds[i] = (byte) (255*val); x = (i<128) ? i/255.0 : (255-i)/255.0; val = Math.exp(-x*x*8); greens[i] = (byte) (255*val); x = (i<128) ? -1 : (i-156)/255.0; val = Math.exp(-x*x*8); blues[i] = (byte) (255*val); } ColorModel colorModel = new IndexColorModel(8, 256, reds, greens, blues); nrow = data.length; ncol = data[0].length; imagePixels = new byte[nrow * ncol]; for (int i = 0; i < nrow; i++) { byte[] row = data[i]; System.arraycopy(row, 0, imagePixels, i * ncol, ncol); } imageSource = new MemoryImageSource(ncol, nrow,colorModel, imagePixels, 0, ncol); imageSource.setAnimated(true); image = Toolkit.getDefaultToolkit().createImage(imageSource); dirtyImage=false; xmin = 0; xmax = ncol; ymin = nrow; ymax = 0; // zero is on top } /** * Constructs IntegerImage with the given ColorModel and data. * * @param colorModel * @param data */ public ByteImage(ColorModel colorModel, byte[][] data) { nrow = data.length; ncol = data[0].length; imagePixels = new byte[nrow * ncol]; for (int i = 0; i < nrow; i++) { byte[] row = data[i]; System.arraycopy(row, 0, imagePixels, i * ncol, ncol); } imageSource = new MemoryImageSource(ncol, nrow,colorModel, imagePixels, 0, ncol); imageSource.setAnimated(true); image = Toolkit.getDefaultToolkit().createImage(imageSource); dirtyImage=false; xmin = 0; xmax = ncol; ymin = nrow; ymax = 0; // zero is on top } /** * Sets new values assuming that the integer array has not changed. * * @param val */ public void updateImage(byte[][] val) { for (int i = 0; i < nrow; i++) { byte[] row = val[i]; System.arraycopy(row, 0, imagePixels, i * ncol, ncol); } // image width is ncol and image height is nrow imageSource.newPixels(0, 0,ncol,nrow); // not needed? dirtyImage=true; } /** * Sets an offset block to new values. * * @param row_offset * @param col_offset * @param val */ public void setBlock(int row_offset, int col_offset, byte[][] val) { if(val==null) return; int block_nrow = val.length; int block_ncol = val[0].length; if ((row_offset < 0) || (row_offset + block_nrow > nrow)) { throw new IllegalArgumentException( "Row index out of range in IntegerImage setBlock."); //$NON-NLS-1$ } if ((col_offset < 0) || (col_offset + block_ncol > ncol)) { throw new IllegalArgumentException( "Column index out of range in IntegerImage setBlock."); //$NON-NLS-1$ } for (int ir =0; ir < block_nrow; ir++) { byte[] row=val[ir]; int index = (ir+row_offset) * ncol +col_offset; System.arraycopy(row, 0, imagePixels, index, block_ncol); } // image width is ncol and image height is nrow // imageSource.newPixels(col_offset, row_offset,block_ncol,block_nrow); // not needed? dirtyImage=true; } /** * Sets array elements in a row to new values. * * @param row * @param val */ public void setRow(int row, byte[] val) { if(val==null) return; if ((row < 0) || (row >= nrow)) { throw new IllegalArgumentException("Row index out of range in IntegerImage setRow."); //$NON-NLS-1$ } if (val.length > ncol) { throw new IllegalArgumentException( "Column index out of range in IntegerImage setRow."); //$NON-NLS-1$ } System.arraycopy(val, 0, imagePixels, row * ncol, val.length); // image width is ncol and image height is nrow // imageSource.newPixels(0,row,val.length,1); // not needed? dirtyImage=true; } /** * Sets a column to new values. * * @param col * @param val */ public void setCol(int col, byte[] val) { if(val==null) return; if (val.length > nrow) { throw new IllegalArgumentException( "Row index out of range in IntegerImage setCol."); //$NON-NLS-1$ } if ((col < 0) || (col >= ncol)) { throw new IllegalArgumentException( "Column index out of range in IntegerImage setCol."); //$NON-NLS-1$ } for (int rindex = 0, nr = val.length; rindex < nr; rindex++) { imagePixels[rindex * ncol + col] = val[rindex]; } // image width is ncol and image height is nrow // imageSource.newPixels(col,0,1,val.length); // not needed? dirtyImage=true; } /** * Sets a cell to a new value. */ public void setCell(int row, int col, byte val) { imagePixels[row * ncol + col]=val; imageSource.newPixels(row, col,1,1); dirtyImage=true; } /** * Draws the image. * * @param panel * @param g */ public void draw(DrawingPanel panel, Graphics g) { if (!visible) { return; } if(dirtyImage){ image = Toolkit.getDefaultToolkit().createImage(imageSource); } if (image == null) { panel.setMessage(DisplayRes.getString("Null Image")); //$NON-NLS-1$ return; } Graphics2D g2 = (Graphics2D) g; AffineTransform gat = g2.getTransform(); // save graphics transform RenderingHints hints = g2.getRenderingHints(); if (!OSPRuntime.isMac()) { // Rendering hint bug in Mac Snow Leopard g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); } double sx = (xmax - xmin) * panel.xPixPerUnit / ncol; double sy = (ymax - ymin) * panel.yPixPerUnit / nrow; // translate origin to pixel location of (xmin,ymax) g2.transform(AffineTransform.getTranslateInstance(panel.leftGutter + panel.xPixPerUnit * (xmin - panel.xmin), panel.topGutter + panel.yPixPerUnit * (panel.ymax - ymax))); g2.transform(AffineTransform.getScaleInstance(sx, sy)); // scales image to world units g2.drawImage(image, 0, 0, panel); g2.setTransform(gat); // restore graphics conext g2.setRenderingHints(hints); // restore the hints } public boolean isMeasured() { if (image == null) { return false; } return true; } public double getXMin() { return xmin; } public double getXMax() { return xmax; } public double getYMin() { return ymin; } public double getYMax() { return ymax; } public void setXMin(double _xmin) { xmin = _xmin; } public void setXMax(double _xmax) { xmax = _xmax; } public void setYMin(double _ymin) { ymin = _ymin; } public void setYMax(double _ymax) { ymax = _ymax; } public void setMinMax(double _xmin, double _xmax, double _ymin, double _ymax) { xmin = _xmin; xmax = _xmax; ymin = _ymin; ymax = _ymax; } } /* * Open Source Physics software is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be * released under the GNU GPL license. * * This software is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this; if not, write to the Free Software Foundation, Inc., 59 Temple Place, * Suite 330, Boston MA 02111-1307 USA or view the license online at * http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */