/**************************************************************************** Copyright (c) 2004, Colorado School of Mines and others. All rights reserved. This program and accompanying materials are made available under the terms of the Common Public License - v1.0, which accompanies this distribution, and is available at http://www.eclipse.org/legal/cpl-v10.html ****************************************************************************/ package edu.mines.jtk.mosaic; import java.awt.*; import java.awt.image.*; import edu.mines.jtk.awt.*; import edu.mines.jtk.dsp.Sampling; import edu.mines.jtk.util.*; import static edu.mines.jtk.util.ArrayMath.*; /** * A view of sampled functions f(x1,x2), displayed as a 2-D array of pixels. * Function values are converted to pixel colors in two steps. In the first * step, these values are converted to unsigned bytes in the range [0,255]. * In the second step, those bytes are converted to pixel colors. *
* The first step is a mapping from function values to unsigned byte values. * This mapping is linear, except for clipping, which ensures that no byte * value lies outside the range [0,255]. The linear mapping is defined by * two clip values, clipMin and clipMax. The minimum clip value clipMin * corresponds to byte value 0, and the maximum clip value clipMax * corresponds to byte value 255. Sample values less than clipMin are * mapped to 0; sample values greater than clipMax are mapped to 255. *
* Byte values are computed for every pixel displayed by this view. Because * the view typically contains more pixels than samples, this first mapping * often requires interpolation between sampled values of functions f(x1,x2). * Either linear or nearest-neighbor interpolation may be specified for this * first step. *
* The second step depends on the number (one, three, or four) of sampled * functions specified. For one function, the byte values are indices for * a table of 256 colors (a color map) in a specified index color model. * See {@link edu.mines.jtk.awt.ColorMap} for more details. *
* For three (or four) sampled functions, the byte values are interpreted * directly as color components red, green, and blue (and alpha). In this * case, any indexed color model specified is not used. The number of color * components equals the number of sampled functions specified. * * @author Dave Hale, Colorado School of Mines * @version 2005.09.27 */ public class PixelsView extends TiledView implements ColorMapped { /** * Orientation of sample axes x1 and x2. For example, the default * orientation X1RIGHT_X2UP corresponds to x1 increasing horizontally * from left to right, and x2 increasing vertically from bottom to top. */ public enum Orientation { X1RIGHT_X2UP, X1DOWN_X2RIGHT } /** * Method used to interpolate pixels between samples of f(x1,x2). * The method NEAREST simply uses the nearest sample value. The method * LINEAR linearly interpolates the four nearest sample values. The * default interpolation method is LINEAR. */ public enum Interpolation { NEAREST, LINEAR } /** * Constructs a pixels view of the specified sampled function f(x1,x2). * Assumes zero first sample values and unit sampling intervals. * Function values are converted to colors using an index color model. * @param f array[n2][n1] of sampled function values f(x1,x2). */ public PixelsView(float[][] f) { this(new float[][][]{f}); } /** * Constructs a pixels view of the specified sampled functions f(x1,x2). * Assumes zero first sample values and unit sampling intervals. * Function values are converted to colors using a direct color model. * @param f array[nc][n2][n1] of sampled function values f(x1,x2), * where nc is the number (three or four) of color components. */ public PixelsView(float[][][] f) { set(f); } /** * Constructs a pixels view of the specified sampled function f(x1,x2). * Function values are converted to colors using an index color model. * @param s1 the sampling of the variable x1; must be uniform. * @param s2 the sampling of the variable x2; must be uniform. * @param f array[n2][n1] of sampled function values f(x1,x2). */ public PixelsView(Sampling s1, Sampling s2, float[][] f) { this(s1,s2,new float[][][]{f}); } /** * Constructs a pixels view of the specified sampled functions f(x1,x2). * Assumes zero first sample values and unit sampling intervals. * Function values are converted to colors using a direct color model. * @param s1 the sampling of the variable x1; must be uniform. * @param s2 the sampling of the variable x2; must be uniform. * @param f array[nc][n2][n1] of sampled function values f(x1,x2), * where nc is the number (three or four) of color components. */ public PixelsView(Sampling s1, Sampling s2, float[][][] f) { set(s1,s2,f); } /** * Sets the sampled function f(x1,x2) for this view. * If compatible samplings for x1 and x2 have already been specified, * then this method uses them. Otherwise, this method assumes zero * first sample values and unit sampling intervals. * Function values are converted to colors using an index color model. * @param f array[n2][n1] of sampled function values f(x1,x2). */ public void set(float[][] f) { set(new float[][][]{f}); } /** * Sets the sampled functions f(x1,x2) for this view. * If compatible samplings for x1 and x2 have already been specified, * then this method uses them. Otherwise, this method assumes zero * first sample values and unit sampling intervals. * Function values are converted to colors using a direct color model. * @param f array[nc][n2][n1] of sampled function values f(x1,x2), * where nc is the number (three or four) of color components. */ public void set(float[][][] f) { if (_s1!=null && _s1.getCount()==f[0][0].length && _s2!=null && _s2.getCount()==f[0].length) { set(_s1,_s2,f); } else { set(new Sampling(f[0][0].length),new Sampling(f[0].length),f); } } /** * Sets the sampled function f(x1,x2) for this view. * Function values are converted to colors using an index color model. * @param s1 the sampling of the variable x1; must be uniform. * @param s2 the sampling of the variable x2; must be uniform. * @param f array[n2][n1] of sampled function values f(x1,x2). */ public void set(Sampling s1, Sampling s2, float[][] f) { set(s1,s2,new float[][][]{f}); } /** * Sets the sampled functions f(x1,x2) for this view. * Function values are converted to colors using a direct color model. * @param s1 the sampling of the variable x1; must be uniform. * @param s2 the sampling of the variable x2; must be uniform. * @param f array[nc][n2][n1] of sampled function values f(x1,x2), * where nc is the number (three or four) of color components. * This number must equal that when this view was constructed. */ public void set(Sampling s1, Sampling s2, float[][][] f) { Check.argument(s1.isUniform(),"s1 is uniform"); Check.argument(s2.isUniform(),"s2 is uniform"); Check.argument(isRegular(f),"f is regular"); Check.argument(s1.getCount()==f[0][0].length,"s1 consistent with f"); Check.argument(s2.getCount()==f[0].length,"s2 consistent with f"); Check.argument(_nc!=0 || f.length==1 || f.length==3 || f.length==4, "number of sampled functions is one, three, or four"); Check.argument(_nc==0 || _nc==f.length, "number of sampled functions is same as when view constructed"); _nc = f.length; _s1 = s1; _s2 = s2; _f = copy(f); if (_clips==null) { _clips = new Clips[_nc]; for (int ic=0; ic<_nc; ++ic) _clips[ic] = new Clips(_f[ic]); } else { for (int ic=0; ic<_nc; ++ic) _clips[ic].setArray(_f[ic]); } _clipMin = null; _clipMax = null; updateSampling(); repaint(); } /** * Sets the orientation of sample axes. * @param orientation the orientation. */ public void setOrientation(Orientation orientation) { if (_orientation!=orientation) { _orientation = orientation; updateSampling(); repaint(); } } /** * Gets the orientation of sample axes. * @return the orientation. */ public Orientation getOrientation() { return _orientation; } /** * Sets the method for interpolation between samples. * @param interpolation the interpolation method. */ public void setInterpolation(Interpolation interpolation) { if (_interpolation!=interpolation) { _interpolation = interpolation; updateBestProjectors(); repaint(); } } /** * Gets the method for interpolation between samples. * @return the interpolation method. */ public Interpolation getInterpolation() { return _interpolation; } /** * Sets the index color model for this view. For three or four color * components, a direct color model is used instead of this index color * model. The default color model is a black-to-white gray model. * @param colorModel the index color model. */ public void setColorModel(IndexColorModel colorModel) { _colorMap.setColorModel(colorModel); repaint(); } /** * Gets the index color model for this view. * @return the index color model; null, if a direct color model is being * used (for multiple color components) instead of an index color model. */ public IndexColorModel getColorModel() { return (_nc==1)?_colorMap.getColorModel():null; } /** * Sets the clips for this view. A pixels view maps values of the sampled * function f(x1,x2) to bytes, which are then used as indices into a * specified color model. This mapping from sample values to byte indices * is linear, and depends on only these two clip values. The minimum clip * value corresponds to byte index 0, and the maximum clip value corresponds * to byte index 255. Sample values outside of the range (clipMin,clipMax) * are clipped to lie inside this range. *
* Calling this method disables the computation of clips from percentiles. * Any clip values computed or specified previously will be forgotten. *
* If multiple color components, sets clips for all components. * @param clipMin the sample value corresponding to color model index 0. * @param clipMax the sample value corresponding to color model index 255. */ public void setClips(float clipMin, float clipMax) { for (int ic=0; ic<_nc; ++ic) setClips(ic,clipMin,clipMax); } /** * Gets the minimum clip value. If multiple color components, gets the * minimum clip value for only the first color component. * @return the minimum clip value. */ public float getClipMin() { return getClipMin(0); } /** * Gets the maximum clip value. If multiple color components, gets the * maximum clip value for only the first color component. * @return the maximum clip value. */ public float getClipMax() { return _clips[0].getClipMax(); } /** * Sets the percentiles used to compute clips for this view. The default * percentiles are 0 and 100, which correspond to the minimum and maximum * values of the sampled function f(x1,x2). *
* Calling this method enables the computation of clips from percentiles. * Any clip values specified or computed previously will be forgotten. *
* If multiple color components, sets percentiles for all components.
* @param percMin the percentile corresponding to clipMin.
* @param percMax the percentile corresponding to clipMax.
*/
public void setPercentiles(float percMin, float percMax) {
for (int ic=0; ic<_nc; ++ic)
setPercentiles(ic,percMin,percMax);
}
/**
* Gets the minimum percentile. If multiple color components, gets the
* minimum percentile for only the first color component.
* @return the minimum percentile.
*/
public float getPercentileMin() {
return _clips[0].getPercentileMin();
}
/**
* Gets the maximum percentile. If multiple color components, gets the
* maximum percentile for only the first color component.
* @return the maximum percentile.
*/
public float getPercentileMax() {
return _clips[0].getPercentileMax();
}
/**
* Sets the clips for the specified color component.
* @param ic the index (0, 1, 2, or 3) of the color component.
* @param clipMin the sample value corresponding to byte value 0.
* @param clipMax the sample value corresponding to byte value 255.
*/
public void setClips(int ic, float clipMin, float clipMax) {
checkComponent(ic);
_clips[ic].setClips(clipMin,clipMax);
repaint();
}
/**
* Gets the minimum clip value for the specified color component.
* @param ic the index (0, 1, 2, or 3) of the color component.
* @return the minimum clip value.
*/
public float getClipMin(int ic) {
return _clips[ic].getClipMin();
}
/**
* Gets the maximum clip value for the specified color component.
* @param ic the index (0, 1, 2, or 3) of the color component.
* @return the maximum clip value.
*/
public float getClipMax(int ic) {
return _clips[ic].getClipMax();
}
/**
* Sets the percentiles for the specified color component.
* @param ic the index (0, 1, 2, or 3) of the color component.
* @param percMin the percentile corresponding to clipMin.
* @param percMax the percentile corresponding to clipMax.
*/
public void setPercentiles(int ic, float percMin, float percMax) {
_clips[ic].setPercentiles(percMin,percMax);
repaint();
}
/**
* Gets the minimum percentile for the specified color component.
* @param ic the index (0, 1, 2, or 3) of the color component.
* @return the minimum percentile.
*/
public float getPercentileMin(int ic) {
return _clips[ic].getPercentileMin();
}
/**
* Gets the maximum percentile for the specified color component.
* @param ic the index (0, 1, 2, or 3) of the color component.
* @return the maximum percentile.
*/
public float getPercentileMax(int ic) {
return _clips[ic].getPercentileMax();
}
/**
* Adds the specified color map listener.
* @param cml the listener.
*/
public void addColorMapListener(ColorMapListener cml) {
_colorMap.addListener(cml);
}
/**
* Removes the specified color map listener.
* @param cml the listener.
*/
public void removeColorMapListener(ColorMapListener cml) {
_colorMap.removeListener(cml);
}
public void paint(Graphics2D g2d) {
// Ensure clips are correct.
updateClips();
// Projectors and transcaler.
Projector hp = getHorizontalProjector();
Projector vp = getVerticalProjector();
Transcaler ts = getTranscaler();
// Compute the view rectangle in pixels (device coordinates). Each sample
// contributes a rectangle of pixels that corresponds to an area _dx*_dy
// in sampling coordinates. The view rectangle for all _nx*_ny samples is
// then _nx*_dx*_ny*_dy. Projectors and transcaler map this rectangle to
// pixel coordinates.
double vx0 = _fx-0.5*_dx;
double vx1 = _fx+_dx*(_nx-0.5);
double vy0 = _fy-0.5*_dy;
double vy1 = _fy+_dy*(_ny-0.5);
double ux0 = hp.u(vx0);
double ux1 = hp.u(vx1);
double uy0 = vp.u(vy0);
double uy1 = vp.u(vy1);
double uxmin = min(ux0,ux1);
double uxmax = max(ux0,ux1);
double uymin = min(uy0,uy1);
double uymax = max(uy0,uy1);
int xd = ts.x(uxmin);
int yd = ts.y(uymin);
int wd = ts.width(uxmax-uxmin);
int hd = ts.height(uymax-uymin);
Rectangle viewRect = new Rectangle(xd,yd,wd,hd);
// Restrict drawing to intersection of view and clip rectangles.
Rectangle clipRect = g2d.getClipBounds();
if (clipRect==null)
clipRect = viewRect;
clipRect = clipRect.intersection(viewRect);
if (clipRect.isEmpty())
return;
// Sample coordinates corresponding to the clip rectangle.
int xc = clipRect.x;
int yc = clipRect.y;
int wc = clipRect.width;
int hc = clipRect.height;
double xu = ts.x(xc);
double yu = ts.y(yc);
double wu = ts.width(wc);
double hu = ts.height(hc);
double x0 = hp.v(xu);
double y0 = vp.v(yu);
double x1 = hp.v(xu+wu);
double y1 = vp.v(yu+hu);
// Image pixel sampling.
int nx = wc;
int ny = hc;
int nxy = nx*ny;
double dx = (x1-x0)/max(1,nx-1);
double dy = (y1-y0)/max(1,ny-1);
double fx = x0;
double fy = y0;
// If only one component (using index color model) ...
if (_nc==1) {
float[][] f = _f[0];
float clipMin = _clipMin[0];
float clipMax = _clipMax[0];
byte[] b = (_interpolation==Interpolation.LINEAR) ?
interpolateImageBytesLinear(f,clipMin,clipMax,nx,dx,fx,ny,dy,fy) :
interpolateImageBytesNearest(f,clipMin,clipMax,nx,dx,fx,ny,dy,fy);
ColorModel cm = _colorMap.getColorModel();
DataBuffer db = new DataBufferByte(b,nxy,0);
int dt = DataBuffer.TYPE_BYTE;
int[] bm = new int[]{0xff};
SampleModel sm = new SinglePixelPackedSampleModel(dt,nx,ny,bm);
WritableRaster wr = Raster.createWritableRaster(sm,db,null);
BufferedImage bi = new BufferedImage(cm,wr,false,null);
g2d.drawImage(bi,xc,yc,null);
}
// else, if three or four components (using direct color model) ...
else {
byte[][] b = new byte[_nc][];
for (int ic=0; ic<_nc; ++ic) {
float[][] f = _f[ic];
float clipMin = _clipMin[ic];
float clipMax = _clipMax[ic];
b[ic] = (_interpolation==Interpolation.LINEAR) ?
interpolateImageBytesLinear(f,clipMin,clipMax,nx,dx,fx,ny,dy,fy) :
interpolateImageBytesNearest(f,clipMin,clipMax,nx,dx,fx,ny,dy,fy);
}
ColorModel cm;
int[] bm;
int[] i = new int[nxy];
if (_nc==3) {
cm = new DirectColorModel(24,
0x00ff0000,
0x0000ff00,
0x000000ff);
bm = new int[]{
0x00ff0000,
0x0000ff00,
0x000000ff,
};
byte[] b0 = b[0], b1 = b[1], b2 = b[2];
for (int ixy=0; ixy