Posted by
Shawn Mikula-2 on
Jun 05, 2007; 3:52am
URL: http://imagej.273.s1.nabble.com/Help-Requested-for-Multi-Resolution-Image-Viewer-Plugin-tp3699233.html
I would like to have an ImageJ plugin for viewing multiresolution images
(such images tend to be too large to load completely into memory; hence the
need for breaking them into multiresolution pieces). I currently have the
Java source for interactive viewing of multiresolution images, which
compiles fine, but have been unable to compile this code as an ImageJ
plugin, which would be ideal since that would permit the application of all
of ImageJ's analysis routines on multiresolution images, which otherwise
would be difficult to work with. I'm not certain, but I think part of the
problem with compiling the plugin may be due to the fact that the source
requires Java Advanced Imaging. If anyone can help me out here, it would
be much appreciated. Or if you want to code up the plugin yourself, please
do. Here's the source (composed of two files), which has a hard-coded
multiresolution URL that is loaded by default (only for demonstration
purposes), but which the user should specify themselves. The code (and
associated icon images for interactive navigation through the
multiresolution image) may be downloaded from
http://brainmaps.org/TiledViewer.tgzto compile:
javac TiledViewerPanel.java
to execute:
java TiledViewerPanel (note that you may have to include JAI classpaths)
Thanks.
Shawn
FILE TiledViewer.java
=============================================
/*
* TiledViewer.java
*
* Created Oct 26, 2005 08:02
*
* display component for the annotator that uses tiled images
*
* Note: this thing uses ints instead of longs for maintaining positions,
* so image size may have to be restricted accordingly
* (i.e., max image size of 2^31 pixels x 2^31 pixels, or about
* 14 exaBytes).
*
* Zoomify labels its images 'level-column-row.jpg'. Level, column, and
* row are all zero-based.
*
*
* Starting conditions:
* imageUL.x = 0
* imageUL.y = 0
* imageBuffUL.x = 0
* imageBuffUL.y = 0
* viewportUL.x = 0
* viewportUL.y = 0
* currentX = 0
* currentY = 0
* tileNumUL.x = 0
* tileNumUL.y = 0
* =================
* imageLR.x = imageWidthInPixels -1
* imageLR.y = imageHeightInPixels -1
* imageBuffLR.x = imageBuffUL.x + (imgBufWidthInTiles *
tileSizeInPixels) -1
* imageBuffLR.y = imageBuffUL.y + (imgBufHeightInTiles *
tileSizeInPixels) -1
* viewportLR.x = viewportUL.x + getWidth() -1
* viewportLR.y = viewportUL.y + getHeight() -1
* currentX = 0
* currentY = 0
* tileNumLR.x = tileNumUL.x + imgBufWidthInTiles -1
* tileNumLR.y = tileNumUL.y + imgBufHeightInTiles -1
*
*
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.util.Date;
import java.util.ArrayList;
import javax.media.jai.*;
import javax.media.jai.operator.ScaleDescriptor;
import javax.swing.*;
import javax.swing.border.*;
import com.sun.image.codec.jpeg.*;
public final class TiledViewer
extends JPanel
implements MouseListener,
MouseMotionListener,
ComponentListener {
private static final boolean DEBUG = true;
private static final int TILES_PER_TILE_GROUP = 256;
private static final int INITIAL_DISPLAY_HEIGHT = 256; // pixels
private static final int INITIAL_DISPLAY_WIDTH = 256; // pixels
private static final int IMG_BUF_WIDTH_IN_TILES = 6;
private static final int IMG_BUF_HEIGHT_IN_TILES = 6;
private static final String ICON_DIR = "Icons/";
public int tileSizeInPixels; // read from server file 'ImageProperties.xml'
public int numLevels; // calc'd from server file "
// levelNumToDisplay corresponds directly to zoomify's level
public int levelNumToDisplay; // 0 <= levelNumToDisplay < numLevels
private BufferedImage imageBuffer;
private BufferedImage[][] tileBuffers; // tileBuffer[col][row]
private int imgBufHeightInTiles;
private int imgBufWidthInTiles;
private Graphics2D imageBuffG2;
private String baseName;
// currentX and currentY are used in paintComponent() to determine ...
// ... where to draw the image on the JPanel
private int currentX; // of UL corner in JPanel coordinates
private int currentY; // of UL corner in JPanel coordinates
private int fullImageWidthInPixels; // from server file "
private int fullImageHeightInPixels; // from server file "
private int numTiles; // from server file "
private int imageWidthInPixels; // of the (possibly) reduced image we show
private int imageHeightInPixels; // of the (possibly) reduced image we show
private int x0; // x-coord at start of drag: set in mousePressed
private int y0; // y-coord at start of drag: set in mousePressed
// imageUL and imageLR (in pixels) are constant for a given image and level
private Point imageUL; // UL of image: always 0,0
private Point imageLR; // LR of image: always width-1, height-1
// imageBuffUL and imageBuffLR (in pixels) change with buffer fetches
private Point imageBuffUL; // UL of imageBuffer in IMAGE coordinates
private Point imageBuffLR; // LR of imageBuffer in IMAGE coordinates
// viewportUL and viewportLR change as the user scrolls the viewport AND ...
// ... as buffer fetches occur (measured in pixels) and is relative ...
// ... to the JPanel: viewportUL.x = - currentX, and ...
// ... viewportUL.y = - currentY
private Point viewportUL; // UL of displayed image--the viewport
private Point viewportLR; // LR of displayed image--the viewport
// imgViewportUL and imgViewportLR are the bounds of the viewport ...
// ... expressed in image coordinates
private Point imgViewportUL;
private Point imgViewportLR;
// tileNumberUL and tileNumberLR change as buffer fetches occur
private Point tileNumberUL; // x = col num, y = row num of UL tile in buffer
private Point tileNumberLR; // x = col num, y = row num of LR tile in buffer
private int viewportWidth;
private int viewportHeight;
//
private int rowNumLastTileOfImage;
private int colNumLastTileOfImage;
// number of pixels in the last tile of last col/row respectively at the
// resolution that this image is being shown
private int pixelsInLastCol;
private int pixelsInLastRow;
private boolean isSizeSet; // true if viewport (i.e., JPanel) size is set
private boolean imageDisplayed; // true if buffer-fetching has been done
private boolean isDraggingLeft;
private boolean isDraggingRight;
private boolean isDraggingUp;
private boolean isDraggingDown;
private boolean mustRefresh;
private boolean isScaleFactorSet;
private double xScaleFactor;
private double yScaleFactor;
private double xOffset;
private double yOffset;
private double scaledX;
private double scaledY;
private Cursor normalCursor;
private Cursor waitCursor;
private Cursor moveCursor;
private Cursor crosshairCursor;
private TierData[] tiers; // array index is Zoomify tier number
private int lastTileGroupIndex;
private TiledViewerPanel tiledViewerPanel;
// (imageDrawn == true) => the buffer has been materialized and drawn
private boolean imageDrawn;
// (backBufDrawn == true) => image drawn into offscreen buffer
private boolean backBufDrawn;
// (annotationsDrawn == true) => all annots drawn into offscreen buffer
private boolean annotationsDrawn;
// (bitBltDone == true) => offscreen buffer drawn onto onscreen buffer
private boolean bitBltDone;
/* following are called by methods of the same name in TiledAnnotPanel */
public int getReductionFactor() {
return tiers[levelNumToDisplay].reductionFactor;
} // getReductionFactor
public TiledViewer(TiledViewerPanel pTVP) {
super();
tiledViewerPanel = pTVP;
setOpaque(true);
setBackground(Color.WHITE);
baseName = null;
imageUL = new Point();
imageLR = new Point();
imageBuffUL = new Point();
imageBuffLR = new Point();
viewportUL = new Point();
viewportLR = new Point();
imgViewportUL = new Point();
imgViewportLR = new Point();
tileNumberUL = new Point();
tileNumberLR = new Point();
addMouseListener(this);
addMouseMotionListener(this);
addComponentListener(this);
isSizeSet = false;
imageDisplayed = false;
mustRefresh = true;
isScaleFactorSet = false;
normalCursor = Cursor.getDefaultCursor();
waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
moveCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
crosshairCursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
initComponents();
} // constructor
/*
* displayNewImage: used to display an image that is NOT currently
* displayed.
*
*/
public void displayNewImage(String pImageUrl, int pLevel) {
double _pctFullSize;
PlanarImage _pi;
BufferedImage _bi;
URL _url;
baseName = pImageUrl;
initImageProperties(pLevel);
initImageBuffers(pLevel);
if (tiledViewerPanel != null) {
_pctFullSize = 1.0 /(double)tiers[pLevel].reductionFactor;
tiledViewerPanel.showPercentFullSize(_pctFullSize);
}
loadImageBuffers(0, 0, pLevel);
imageBuffUL.x = 0;
imageBuffUL.y = 0;
imageBuffLR.x = tileSizeInPixels * imgBufWidthInTiles - 1;
imageBuffLR.y = tileSizeInPixels * imgBufHeightInTiles - 1;
if (tiledViewerPanel != null) {
try {
_url = new URL(pImageUrl +"/TileGroup0/0-0-0.jpg");
_pi = JAI.create("URL", _url);
_bi = _pi.getAsBufferedImage();
tiledViewerPanel.changeImage(_bi, imageWidthInPixels,
imageHeightInPixels,new Point(0,0),
new Point(imageWidthInPixels, imageHeightInPixels));
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
}
imageDisplayed = true;
mustRefresh = true;
regenerateDisplay();
} // displayNewImage
private final int constrain(int pVal, int pMax) {
int _retVal;
if (pVal < 0) {
_retVal = 0;
} else if (pVal > pMax) {
_retVal = pMax;
} else {
_retVal = pVal;
}
return _retVal;
} // constrain
/*
* calcNewBuffUL: pUL => UL in reduced IMAGE coordinates
* pNewLevel => the new level to display
*
* Returns: retVal.x = col tileNumber of suitable image buffer UL
* retVal.y = row tileNumber of suitable image buffer UL
*/
private final Point calcNewBuffUL(Point pUL, int pNewLevel) {
Point _retVal;
int _xTileNum;
int _yTileNum;
_xTileNum = constrain(pUL.x / tileSizeInPixels,
(tiers[pNewLevel].numCols -
tiers[pNewLevel].imgBufWidthInTiles));
_yTileNum = constrain(pUL.y / tileSizeInPixels,
(tiers[pNewLevel].numRows -
tiers[pNewLevel].imgBufHeightInTiles));
_retVal = new Point(_xTileNum, _yTileNum);
return _retVal;
} // calcNewBuffUL
/******************************
* displayNewLevel: use to display a DIFFERENT LEVEL of an image
* that is *ALREADY* shown. The location of the
* image at the center of the window will be the
* same before and after this method call.
*
*/
public void displayNewLevel(int pNewLevel) {
double _pctFullSize;
Point _bufTileUL;
double _scaleFactor;
Point _currentCenter;
Point _desiredCenter;
Point _imgViewportUL;
int _currLevel;
_currLevel = levelNumToDisplay;
// get scale factor
if (pNewLevel > _currLevel) {
_scaleFactor = (double) (1 << (pNewLevel - _currLevel));
} else if (pNewLevel < _currLevel) {
_scaleFactor = 1.0D / (double) (1 << (_currLevel - pNewLevel));
} else {
return; // same level: nothing to do
}
// get center of current viewport in reduced image coords
_currentCenter = new Point();
_currentCenter.x = (imgViewportUL.x + imgViewportLR.x)/2;
_currentCenter.y = (imgViewportUL.y + imgViewportLR.y)/2;
// get center of new viewport in reduced image coords
_desiredCenter = new Point();
_desiredCenter.x = (int) (_currentCenter.x * _scaleFactor);
_desiredCenter.y = (int) (_currentCenter.y * _scaleFactor);
// get UL of new viewport in reduced image coords
_imgViewportUL = new Point();
_imgViewportUL.x = _desiredCenter.x - viewportWidth/2;
_imgViewportUL.y = _desiredCenter.y - viewportHeight/2;
// check for and set any new imageBuffer parameters
if ((tiers[_currLevel].imgBufHeightInTiles
!= tiers[pNewLevel].imgBufHeightInTiles) ||
(tiers[_currLevel].imgBufWidthInTiles
!= tiers[pNewLevel].imgBufWidthInTiles)) {
initImageBuffers(pNewLevel);
}
// get new tileNumberUL suitable for the new viewport center
_bufTileUL = calcNewBuffUL(_imgViewportUL, pNewLevel);
// get new viewport UL in JPanel coords
imageBuffUL.x = _bufTileUL.x * tileSizeInPixels;
imageBuffUL.y = _bufTileUL.y * tileSizeInPixels;
viewportUL.x = _imgViewportUL.x - imageBuffUL.x;
viewportUL.y = _imgViewportUL.y - imageBuffUL.y;
// set state variables based on new level
currentX = -viewportUL.x;
currentY = -viewportUL.y;
imgBufHeightInTiles = tiers[pNewLevel].imgBufHeightInTiles;
imgBufWidthInTiles = tiers[pNewLevel].imgBufWidthInTiles;
imageWidthInPixels = tiers[pNewLevel].imageWidthInPixels;
imageHeightInPixels = tiers[pNewLevel].imageHeightInPixels;
rowNumLastTileOfImage = tiers[pNewLevel].rowNumLastTileOfImage;
colNumLastTileOfImage = tiers[pNewLevel].colNumLastTileOfImage;
tileNumberUL = _bufTileUL;
tileNumberLR.x = tileNumberUL.x + imgBufWidthInTiles - 1;
tileNumberLR.y = tileNumberUL.y + imgBufHeightInTiles - 1;
imageUL.x = 0;
imageLR.y = 0;
imageLR.x = imageWidthInPixels - 1;
imageLR.y = imageHeightInPixels - 1;
imageBuffLR.x = imageBuffUL.x + (imgBufWidthInTiles *
tileSizeInPixels)-1;
imageBuffLR.y = imageBuffUL.y + (imgBufHeightInTiles*
tileSizeInPixels)-1;
viewportLR.x = viewportUL.x + viewportWidth - 1;
viewportLR.y = viewportUL.y + viewportHeight - 1;
imgViewportUL = _imgViewportUL;
imgViewportLR.x = imgViewportUL.x + viewportWidth - 1;
imgViewportLR.y = imgViewportUL.y + viewportHeight - 1;
levelNumToDisplay = pNewLevel;
// fetch new tiles for the buffer
loadImageBuffers(tileNumberUL.x, tileNumberUL.y, levelNumToDisplay);
// call repaint()
if (tiledViewerPanel != null) {
_pctFullSize = 1.0 /(double)tiers[pNewLevel].reductionFactor;
tiledViewerPanel.showPercentFullSize(_pctFullSize);
tiledViewerPanel.setNewZoom(imgViewportUL, imgViewportLR,
imageWidthInPixels);
}
imageDisplayed = true;
mustRefresh = true;
regenerateDisplay();
repaint();
} // displayNewLevel
public void setScaleFactors(double pXScaleFactor, double pYScaleFactor,
double pXOffset, double pYOffset) {
xScaleFactor = pXScaleFactor;
yScaleFactor = pYScaleFactor;
xOffset = pXOffset;
yOffset = pYOffset;
isScaleFactorSet = true;
} // setScaleFactors
public final double getScaledX() {
return scaledX;
} // getScaledX
public final double getScaledY() {
return scaledY;
} // getScaledY
private void initComponents() {
// max size is entire image buffer
setMaximumSize(new Dimension(tileSizeInPixels * imgBufWidthInTiles,
tileSizeInPixels * imgBufHeightInTiles));
// don't set preferredSize, let UI determine it
// min size is size of a single tile
setMinimumSize(new Dimension (tileSizeInPixels, tileSizeInPixels));
} // initComponents
private boolean loadImageBuffers(int pCol, int pRow, int pLevel) {
boolean _retVal;
int _r;
int _c;
if (baseName == null) {
System.err.println("basename = null");
_retVal = false;
} else {
_retVal = true;
_r = pRow;
for (int _row = 0; _row < tiers[pLevel].imgBufHeightInTiles; _row++) {
_c = pCol;
for (int _col = 0; _col < tiers[pLevel].imgBufWidthInTiles;_col++) {
tileBuffers[_col][_row] = getImageTile(_c, _r, pLevel);
if (tileBuffers[_col][_row] == null) {
_retVal = false;
System.out.println("loadImageBuffers() failed: col="+_col+
", row="+_row+", pLevel="+pLevel);
}
_c++;
}
_r++;
}
}
return _retVal;
} // loadImageBuffers
private final boolean atLastColumn() {
return tileNumberLR.x == colNumLastTileOfImage;
} // atLastColumn
private final boolean atLastRow() {
return tileNumberLR.y == rowNumLastTileOfImage;
} // atLastRow
private BufferedImage getImageTile(int pCol, int pRow, int pLevel) {
BufferedImage _retVal;
PlanarImage _pi;
String _tileUrlString;
URL _url;
_retVal = null;
_tileUrlString = getFlashFileName(pCol, pRow, pLevel);
try {
_url = new URL(_tileUrlString); // 0 mS always
_pi = JAI.create("URL", _url); // 1600 ms 1st time, 1 mS after
_retVal = _pi.getAsBufferedImage();// 2166 ms 1st time, then variable
} catch (Exception e) {
e.printStackTrace();
}
return _retVal;
} // getImageTile
private String getFlashFileName(int pCol, int pRow, int pLevel) {
int _tileNum;
int _tileGroupNum;
String _retVal;
_tileNum = pCol + (pRow * tiers[pLevel].numCols);
if (_tileNum <= tiers[pLevel].tilesInStartGroup - 1) {
_tileGroupNum = tiers[pLevel].startingTileGroup;
} else if (_tileNum >
(tiers[pLevel].numTiles - tiers[pLevel].tilesInEndGroup)) {
_tileGroupNum = tiers[pLevel].endingTileGroup;
} else {
_tileNum = _tileNum - tiers[pLevel].tilesInStartGroup;
_tileGroupNum = tiers[pLevel].startingTileGroup;
_tileGroupNum += 1 + _tileNum / TILES_PER_TILE_GROUP;
}
_retVal = baseName + "/TileGroup" +_tileGroupNum+ "/"
+pLevel+ "-" +pCol+ "-" +pRow+ ".jpg";
return _retVal;
} // getFlashFileName
private int decodeXMLProp(StringBuffer pPropBuff, String pProp) {
int _numPos;
int _quotePos;
_numPos = pProp.length()+ 2 + pPropBuff.indexOf(pProp);
_quotePos = pPropBuff.indexOf("\"", _numPos);
return Integer.parseInt(pPropBuff.substring(_numPos, _quotePos));
} // decodeXMLProp
/*
* read the Flash ImageProperties.xml file and set the following
* instance vars:
* fullImageWidthInPixels, fullImageHeightInPixels,
* numTiles, tileSizeInPixels, numLevels, imageWidthInPixels,
* imageHeightInPixels, imgBufWidthInTiles, imgBufHeightInTiles,
* imageBuffUL, imageBuffLR
*/
private void initImageProperties(int pLevel) {
URL _url;
InputStream _is;
StringBuffer _sb;
String _propStr;
double _largestDim;
int _numRows;
int _numCols;
int _reductionFactor;
ArrayList _tiers;
TierData _tierData;
_sb = new StringBuffer(130);
try {
_url = new URL(baseName+"/ImageProperties.xml");
//System.err.println(_url);
_is = (InputStream) _url.getContent();
while (_is.available() > 0) {
_sb.append((char) _is.read());
}
_is.close();
_propStr = _sb.toString();
//System.err.println(_propStr);
} catch (FileNotFoundException fnfe) {
System.err.println("This probably isn't a Flash image.");
return;
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null,
"Connection refused: "+baseName,
"Connection refused",
JOptionPane.ERROR_MESSAGE);
return;
}
fullImageWidthInPixels = decodeXMLProp(_sb, "WIDTH");
fullImageHeightInPixels = decodeXMLProp(_sb, "HEIGHT");
numTiles = decodeXMLProp(_sb, "NUMTILES");
tileSizeInPixels = decodeXMLProp(_sb, "TILESIZE");
lastTileGroupIndex = numTiles / TILES_PER_TILE_GROUP;
if ((numTiles % TILES_PER_TILE_GROUP) == 0) {
lastTileGroupIndex--;
}
_largestDim = Math.max(fullImageWidthInPixels, fullImageHeightInPixels);
_tiers = new ArrayList();
numLevels = 1;
while (((int) _largestDim) > tileSizeInPixels) {
numLevels++;
_largestDim /= 2;
}
_reductionFactor = 1;
for (int i = numLevels-1; i >= 0; i--) {
_tierData = new TierData();
_tierData.reductionFactor = _reductionFactor;
_tierData.imageWidthInPixels =
fullImageWidthInPixels / _reductionFactor;
_tierData.imageHeightInPixels =
fullImageHeightInPixels / _reductionFactor;
_tierData.numRows = (int) Math.ceil(((double)
_tierData.imageHeightInPixels) / tileSizeInPixels);
_tierData.numCols = (int) Math.ceil(((double)
_tierData.imageWidthInPixels) / tileSizeInPixels);
_tierData.numTiles = _tierData.numRows * _tierData.numCols;
_tierData.rowNumLastTileOfImage = _tierData.numRows - 1;
_tierData.colNumLastTileOfImage = _tierData.numCols - 1;
_tierData.pixelsInLastCol
= _tierData.imageWidthInPixels % tileSizeInPixels;
if (_tierData.pixelsInLastCol == 0) {
_tierData.pixelsInLastCol = 256;
}
_tierData.pixelsInLastRow
= _tierData.imageHeightInPixels % tileSizeInPixels;
if (_tierData.pixelsInLastRow == 0) {
_tierData.pixelsInLastRow = 256;
}
_tierData.imgBufWidthInTiles
= (int) Math.min(_tierData.numCols, IMG_BUF_WIDTH_IN_TILES);
_tierData.imgBufHeightInTiles
= (int) Math.min(_tierData.numRows, IMG_BUF_HEIGHT_IN_TILES);
_tiers.add(0, _tierData);
_reductionFactor = _reductionFactor << 1;
}
tiers = new TierData[numLevels];
tiers = (TierData[]) _tiers.toArray(tiers);
initTiers();
initLevel(pLevel);
} // initImageProperties
private void initTiers() {
int _startingTileGroup;
int _tileTotal;
int _startGroupTileCount;
int _remTiles;
// assign starting and ending group numbers to tiers
_startingTileGroup = 0;
_tileTotal = 0;
for (int i = 0; i < numLevels; i++) {
tiers[i].startingTileGroup = _startingTileGroup;
_tileTotal += tiers[i].numTiles;
tiers[i].endingTileGroup = _tileTotal / TILES_PER_TILE_GROUP;
_startingTileGroup = tiers[i].endingTileGroup;
}
// assign start and end tile counts to tile groups
_startGroupTileCount = 0;
for (int i = 0; i < numLevels; i++) {
if ((_startGroupTileCount + tiers[i].numTiles)
< TILES_PER_TILE_GROUP) {
tiers[i].tilesInStartGroup = tiers[i].numTiles;
tiers[i].tilesInEndGroup = tiers[i].numTiles;
_startGroupTileCount += tiers[i].numTiles;
} else if ((_startGroupTileCount + tiers[i].numTiles) ==
TILES_PER_TILE_GROUP) {
tiers[i].tilesInStartGroup = tiers[i].numTiles;
tiers[i].tilesInEndGroup = tiers[i].numTiles;
_startGroupTileCount = 0;
} else { // (_startGroupTileCount + tiers[i].numTiles) > 256
tiers[i].tilesInStartGroup
= TILES_PER_TILE_GROUP - _startGroupTileCount;
_remTiles = tiers[i].numTiles - tiers[i].tilesInStartGroup;
tiers[i].tilesInEndGroup = _remTiles % TILES_PER_TILE_GROUP;
if (tiers[i].tilesInEndGroup == 0) { // exactly fits
tiers[i].tilesInEndGroup = TILES_PER_TILE_GROUP;
_startGroupTileCount = 0;
} else {
_startGroupTileCount = tiers[i].tilesInEndGroup;
}
}
}
} // initTiers
/*
* sets UL corner of everything to 0,0 and LR corner correspondingly
*/
private void initLevel(int pLevel) {
levelNumToDisplay = pLevel;
imageWidthInPixels = tiers[pLevel].imageWidthInPixels;
imageHeightInPixels = tiers[pLevel].imageHeightInPixels;
imgBufWidthInTiles = tiers[pLevel].imgBufWidthInTiles;
imgBufHeightInTiles = tiers[pLevel].imgBufHeightInTiles;
rowNumLastTileOfImage = tiers[pLevel].rowNumLastTileOfImage;
colNumLastTileOfImage = tiers[pLevel].colNumLastTileOfImage;
tileNumberUL.x = 0;
tileNumberUL.y = 0;
tileNumberLR.x = imgBufWidthInTiles - 1;
tileNumberLR.y = imgBufHeightInTiles - 1;
imageUL.x = 0;
imageUL.y = 0;
imageLR.x = imageWidthInPixels - 1;
imageLR.y = imageHeightInPixels - 1;
imageBuffUL.x = 0;
imageBuffUL.y = 0;
imageBuffLR.x = (imgBufWidthInTiles * tileSizeInPixels) - 1;
imageBuffLR.y = (imgBufHeightInTiles * tileSizeInPixels) - 1;
viewportUL.x = 0;
viewportUL.y = 0;
viewportLR.x = getWidth() - 1;
viewportLR.y = getHeight() - 1;
imgViewportUL.x = viewportUL.x;
imgViewportUL.y = viewportUL.y;
imgViewportLR.x = viewportLR.x;
imgViewportLR.y = viewportLR.y;
} // initLevel
private void initImageBuffers(int pLevel) {
imageBuffer = new BufferedImage(
tileSizeInPixels * tiers[pLevel].imgBufWidthInTiles,
tileSizeInPixels * tiers[pLevel].imgBufHeightInTiles,
BufferedImage.TYPE_3BYTE_BGR);
tileBuffers = new BufferedImage[tiers[pLevel].imgBufWidthInTiles]
[tiers[pLevel].imgBufHeightInTiles];
} // initImageBuffers
public final void regenerateDisplay() {
imageDrawn = false;
backBufDrawn = false;
annotationsDrawn = false;
bitBltDone = false;
repaint();
} // regenerateDisplay
// this is the original, prior to adding drawing extensions
public void paintComponent(Graphics pG) {
Graphics2D _g2;
super.paintComponent(pG);
if (!imageDisplayed) {
return;
}
_g2 = (Graphics2D) pG;
// get graphics context for our offscreen imageBuffer
imageBuffG2 = (Graphics2D) imageBuffer.getGraphics();
if (mustRefresh) {
for (int _col = 0; _col < imgBufWidthInTiles; _col++) {
for (int _row = 0; _row < imgBufHeightInTiles; _row++) {
// draw the tiles into the main buffer
imageBuffG2.drawImage(tileBuffers[_col][_row],
_col * tileSizeInPixels,
_row * tileSizeInPixels,
tileBuffers[_col][_row].getWidth(),
tileBuffers[_col][_row].getHeight(),
Color.white,
this);
}
}
mustRefresh = false;
}
// draw the main buffer onto the JPanel
_g2.drawImage(imageBuffer, currentX, currentY, this);
viewportWidth = getWidth();
viewportHeight = getHeight();
} // paintComponent
// from MouseListener
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() > 1) { // re-center image at mouse click
recenterImageAt(e.getX(), e.getY());
}
} // mouseClicked
private final int getFullSizedX(int pPanelX) {
return tiers[levelNumToDisplay].reductionFactor *
(imgViewportUL.x + pPanelX);
} // getFullSizedX
private final int getFullSizedY(int pPanelY) {
return tiers[levelNumToDisplay].reductionFactor *
(imgViewportUL.y + pPanelY);
} // getFullSizedY
private final int getJPanelX(int pFullSizedX) {
return (pFullSizedX / tiers[levelNumToDisplay].reductionFactor)
- imageBuffUL.x;
} // getJPanelX
private final int getJPanelY(int pFullSizedY) {
return (pFullSizedY / tiers[levelNumToDisplay].reductionFactor)
- imageBuffUL.y;
} // getJPanelY
private final int getJPanelWorH(int pFullSizedParam) {
return (pFullSizedParam / tiers[levelNumToDisplay].reductionFactor);
} // getJPanelWorH
// adjust viewport and imageBuffer so that UL = (pX,pY)
// in absolute IMAGE pixel coordinates
public void moveULCornerAbsoluteTo(int pX, int pY) {
Point _uL; // potential new UL tile
Point _lR; // potential new LR tile
_uL = new Point();
_uL.x = pX < 0 ? 0 : (pX / tileSizeInPixels);
_uL.y = pY < 0 ? 0 : (pY / tileSizeInPixels);
// do we need to adjust buffer contents?
if (! _uL.equals(tileNumberUL)) {
// yes: get new buffer data
// calc new column num for tileNumberUL
_lR = new Point(_uL.x + imgBufWidthInTiles - 1,
_uL.y + imgBufHeightInTiles - 1);
//System.out.println("_lR: "+_lR);
if (_lR.x > colNumLastTileOfImage) {
// must clip desired buffer position to stay on image
_lR.x = colNumLastTileOfImage;
_uL.x = _lR.x - (imgBufWidthInTiles - 1);
tileNumberUL.x = _uL.x;
imageBuffUL.x = tileNumberUL.x * tileSizeInPixels;
imgViewportUL.x = pX;
viewportUL.x = pX - imageBuffUL.x;
currentX = -viewportUL.x;
} else { // just move the buffer
//System.out.println("// just move the buffer");
tileNumberUL.x = _uL.x;
imageBuffUL.x = tileNumberUL.x * tileSizeInPixels;
imgViewportUL.x = pX;
viewportUL.x = pX - imageBuffUL.x;
currentX = -viewportUL.x;
}
// then, calc new row num for tileNumberUL
if (_lR.y > rowNumLastTileOfImage) {
_lR.y = rowNumLastTileOfImage;
_uL.y = _lR.y - (imgBufHeightInTiles - 1);
tileNumberUL.y = _uL.y;
imageBuffUL.y = tileNumberUL.y * tileSizeInPixels;
imgViewportUL.y = pY;
viewportUL.y = pY - imageBuffUL.y;
currentY = -viewportUL.y;
} else {
//System.out.println("else part");
tileNumberUL.y = _uL.y;
imageBuffUL.y = tileNumberUL.y * tileSizeInPixels;
imgViewportUL.y = pY;
viewportUL.y = pY - imageBuffUL.y;
currentY = -viewportUL.y;
}
tileNumberLR = _lR;
imageBuffLR.x = (tileNumberLR.x + 1) * tileSizeInPixels - 1;
imageBuffLR.y = (tileNumberLR.y + 1) * tileSizeInPixels - 1;
loadImageBuffers(_uL.x, _uL.y, levelNumToDisplay);
} else { // no: don't change buffer, just move the viewport
viewportUL.x += (pX - imgViewportUL.x);
currentX = -viewportUL.x;
imgViewportUL.x = pX;
viewportUL.y += (pY - imgViewportUL.y);
currentY = -viewportUL.y;
imgViewportUL.y = pY;
} // all adjustments have been made at this point
viewportLR.x = viewportUL.x + getWidth() - 1;
viewportLR.y = viewportUL.y + getHeight() - 1;
imgViewportLR.x = imgViewportUL.x + getWidth() - 1;
imgViewportLR.y = imgViewportUL.y + getHeight() - 1;
if (tiledViewerPanel != null) {
tiledViewerPanel.drawViewport(imgViewportUL, imgViewportLR);
}
mustRefresh = true;
regenerateDisplay();
//repaint();
} // moveULCornerAbsoluteTo
// adjust viewport and imageBuffer so that UL = (pX,pY)
// relative to its CURRENT position
// (pX > 0) => move the viewport pX pixels to the RIGHT
// (py > 0) => move the viewport pY pixels DOWN
public void moveULCornerRelative(int pX, int pY) {
moveULCornerAbsoluteTo(imgViewportUL.x+pX, imgViewportUL.y+pY);
} // moveULCornerRelative
// adjust the image so that its center is in the middle of the viewport
public void recenterImage() {
Point _desiredCenter;
Point _vpCenter;
Point _imgViewportUL;
Point _bufTileUL;
//System.out.println("entering recenterImage()");
// get desired center (i.e., center of *image*) in reduced image coords
_desiredCenter = new Point(imageWidthInPixels/2, imageHeightInPixels/2);
// get center of viewport
_vpCenter = new Point(viewportWidth/2, viewportHeight/2);
// get UL of viewport in reduced image coords
_imgViewportUL = new Point();
_imgViewportUL.x = _desiredCenter.x - viewportWidth/2;
_imgViewportUL.y = _desiredCenter.y - viewportHeight/2;
// get new tileNumberUL suitable for new viewport center
_bufTileUL = calcNewBuffUL(_imgViewportUL, levelNumToDisplay);
// get new viewport UL in JPanel coords
imageBuffUL.x = _bufTileUL.x * tileSizeInPixels;
imageBuffUL.y = _bufTileUL.y * tileSizeInPixels;
viewportUL.x = _imgViewportUL.x - imageBuffUL.x;
viewportUL.y = _imgViewportUL.y - imageBuffUL.y;
// set state variables based on new center
currentX = -viewportUL.x;
currentY = -viewportUL.y;
tileNumberUL = _bufTileUL;
tileNumberLR.x = tileNumberUL.x + imgBufWidthInTiles -1;
tileNumberLR.y = tileNumberUL.y + imgBufHeightInTiles -1;
imageBuffLR.x = imageBuffUL.x + (imgBufWidthInTiles *
tileSizeInPixels)-1;
imageBuffLR.y = imageBuffUL.y + (imgBufHeightInTiles*
tileSizeInPixels)-1;
viewportLR.x = viewportUL.x + viewportWidth -1;
viewportLR.y = viewportUL.y + viewportHeight -1;
imgViewportUL = _imgViewportUL;
imgViewportLR.x = imgViewportUL.x + viewportWidth -1;
imgViewportLR.y = imgViewportUL.y + viewportHeight -1;
// fetch new tiles for the buffer
loadImageBuffers(tileNumberUL.x, tileNumberUL.y, levelNumToDisplay);
// call repaint()
if (tiledViewerPanel != null) {
tiledViewerPanel.drawViewport(imgViewportUL, imgViewportLR);
}
imageDisplayed = true;
mustRefresh = true;
regenerateDisplay();
//repaint();
} // recenterImage
//
// pX and pY are the coords of the point to be centered on the viewport
// in JPanel coords
//
public void recenterImageAt(int pX, int pY) {
int _centerX; // center of the viewport in the X direction
int _centerY; // center of the viewport in the Y direction
int _xlatPx; // the amount to translate in the X and Y directions
int _xlatPy; // from the UL corner of viewport to the mouse click
int _deltaX; // the X and Y amounts used to adjust currentX and
int _deltaY; // currentY prior to drawing
//System.out.println("entering recenterImageAt(" +pX+ "," +pY+ ")");
//printEverything();
// get coords for center of the viewport
_centerX = (viewportUL.x + viewportLR.x) / 2; //JPanel coords
_centerY = (viewportUL.y + viewportLR.y) / 2;
// artificially set the original mouse location to be the
// center of the viewport
x0 = _centerX; //JPanel coords
y0 = _centerY;
// get distance from viewportUL to location of mouse click
_xlatPx = pX + viewportUL.x;
_xlatPy = pY + viewportUL.y;
// get amount to shift
_deltaX = _centerX - _xlatPx;
_deltaY = _centerY - _xlatPy;
// and then shift
currentX += _deltaX;
currentY += _deltaY;
// check for needed buffer updates
boundsCheck(pX, pY);
// update our control vars
viewportUL.x = -currentX; //JPanel coords
viewportUL.y = -currentY;
viewportLR.x = viewportUL.x + getWidth() - 1; //JPanel coords
viewportLR.y = viewportUL.y + getHeight() - 1;
imgViewportUL.x -= _deltaX; // image coords
imgViewportUL.y -= _deltaY;
imgViewportLR.x = imgViewportUL.x + getWidth() - 1; // image coords
imgViewportLR.y = imgViewportUL.y + getHeight() - 1;
if (tiledViewerPanel != null) {
tiledViewerPanel.drawViewport(imgViewportUL, imgViewportLR);
}
//printEverything();
// show the new results
regenerateDisplay();
//repaint();
} // recenterImageAt
public void mousePressed(MouseEvent e) {
this.setCursor(moveCursor);
x0 = e.getX();
y0 = e.getY();
} // mousePressed
public void mouseReleased(MouseEvent e) {
this.setCursor(normalCursor);
} //mouseReleased
public void mouseEntered(MouseEvent e) {} // mouseEntered
public void mouseExited(MouseEvent e) {} // mouseExited
// from MouseMotionListener
/**************************
* boundsCheck: the entry into the row/col buffer fetch
* mechanism. It will transparently handle such fetches; its
* return value is meant to indicate whether or not to scroll
* the image. When the scrolling would go off the image
* boundaries, boundsCheck returns false.
* pX, pY: the x,y coordinates of the mouse while dragging
*/
private boolean boundsCheck(int pX, int pY) {
int _deltaX;
int _deltaY;
// determine where proposed drag will take us, but don't perform drag
_deltaX = pX - x0;
_deltaY = pY - y0;
if (_deltaX > 0) {
isDraggingLeft = false;
isDraggingRight = true;
} else if (_deltaX < 0) {
isDraggingLeft = true;
isDraggingRight = false;
} else { // purely vertical
isDraggingLeft = false;
isDraggingRight = false;
}
if (_deltaY < 0) {
isDraggingUp = true;
isDraggingDown = false;
} else if (_deltaY > 0) {
isDraggingUp = false;
isDraggingDown = true;
} else { // purely horizontal
isDraggingUp = false;
isDraggingDown = false;
}
// now check for new left or right column ...
if (isDraggingRight && (imgViewportUL.x - _deltaX) < imageBuffUL.x) {
this.setCursor(waitCursor);
fetchBufferLeftColumn();
this.setCursor(moveCursor);
} else if (isDraggingLeft &&
(imgViewportLR.x - _deltaX) > imageBuffLR.x) {
this.setCursor(waitCursor);
fetchBufferRightColumn();
this.setCursor(moveCursor);
}
// ... now for new top or bottom row
if (isDraggingDown && (imgViewportUL.y - _deltaY) < imageBuffUL.y) {
this.setCursor(waitCursor);
fetchBufferTopRow();
this.setCursor(moveCursor);
} else if (isDraggingUp &&
(imgViewportLR.y - _deltaY) > imageBuffLR.y) {
this.setCursor(waitCursor);
fetchBufferBottomRow();
this.setCursor(moveCursor);
}
return true;
} // boundsCheck
private void fetchBufferLeftColumn() {
int _shiftDistance;
// don't try to fetch left of image
if (tileNumberUL.x == 0) {
return;
}
//System.out.println("New left");
// move buffers over
for (int _row = 0; _row < imgBufHeightInTiles; _row++) {
for (int _col = imgBufWidthInTiles-1; _col > 0; _col--) {
tileBuffers[_col][_row] = tileBuffers[_col-1][_row];
}
}
// get new column 0
for (int _row = 0; _row < imgBufHeightInTiles; _row++) {
tileBuffers[0][_row] = getImageTile(tileNumberUL.x-1,
_row + tileNumberUL.y,
levelNumToDisplay);
}
// reset the relevant coordinates
tileNumberUL.x--;
tileNumberLR.x--;
_shiftDistance = /*atLastColumn()?pixelsInLastCol :*/ tileSizeInPixels;
imageBuffUL.x -= _shiftDistance;
imageBuffLR.x -= _shiftDistance;
currentX -= _shiftDistance;
mustRefresh = true;
regenerateDisplay();
} // fetchBufferLeftColumn
private void fetchBufferRightColumn() {
int _shiftDistance;
// don't try to fetch past end of image
if (atLastColumn()) {
return;
}
//System.out.println("New right");
// for each row r, move tileBuffer[col][r] to tileBuffer[col-1][r]
for (int _row = 0; _row < imgBufHeightInTiles; _row++) {
for (int _col = 0; _col < imgBufWidthInTiles-1; _col++) {
tileBuffers[_col][_row] = tileBuffers[_col+1][_row];
}
}
// for last col (tileNumberLR.x), fetch new tiles
for (int _row = 0; _row < imgBufHeightInTiles; _row++) {
tileBuffers[imgBufWidthInTiles-1][_row]
= getImageTile(tileNumberLR.x+1,
_row + tileNumberUL.y,
levelNumToDisplay);
}
// reset the relevant coordinates
tileNumberUL.x++;
tileNumberLR.x++;
_shiftDistance = /*atLastColumn()?pixelsInLastCol :*/ tileSizeInPixels;
imageBuffUL.x += _shiftDistance;
imageBuffLR.x += _shiftDistance;
currentX += _shiftDistance;
mustRefresh = true;
regenerateDisplay();
} // fetchBufferRightColumn
private void fetchBufferTopRow() {
int _shiftDistance;
// don't try to fetch above top of image
if (tileNumberUL.y == 0) {
return;
}
//System.out.println("New top");
// move buffers down
for (int _col = 0; _col < imgBufWidthInTiles; _col++) {
for (int _row = imgBufHeightInTiles-1; _row > 0; _row--) {
tileBuffers[_col][_row] = tileBuffers[_col][_row-1];
}
}
// get new row 0
for (int _col = 0; _col < imgBufWidthInTiles; _col++) {
tileBuffers[_col][0]
= getImageTile(_col + tileNumberUL.x,
tileNumberUL.y-1,
levelNumToDisplay);
}
// reset the relevant coordinates
tileNumberUL.y--;
tileNumberLR.y--;
_shiftDistance = /*atLastRow() ? pixelsInLastRow :*/ tileSizeInPixels;
imageBuffUL.y -= _shiftDistance;
imageBuffLR.y -= _shiftDistance;
currentY -= _shiftDistance;
mustRefresh = true;
regenerateDisplay();
} // fetchBufferTopRow
private void fetchBufferBottomRow() {
int _shiftDistance;
// don't try to fetch past bottom of image
if (atLastRow()) {
return;
}
//System.out.println("New bottom");
// move tiles up one row
for (int _col = 0; _col < imgBufWidthInTiles; _col++) {
for (int _row = 0; _row < imgBufHeightInTiles-1; _row++) {
tileBuffers[_col][_row] = tileBuffers[_col][_row+1];
}
}
// for last row (tileNumberLR.y), fetch new tiles
for (int _col = 0; _col < imgBufWidthInTiles; _col++) {
tileBuffers[_col][imgBufHeightInTiles-1]
= getImageTile(_col + tileNumberUL.x,
tileNumberLR.y+1,
levelNumToDisplay);
}
// reset the relevant coordinates
tileNumberUL.y++;
tileNumberLR.y++;
_shiftDistance = /*atLastRow() ? pixelsInLastRow :*/ tileSizeInPixels;
imageBuffUL.y += _shiftDistance;
imageBuffLR.y += _shiftDistance;
currentY += _shiftDistance;
mustRefresh = true;
regenerateDisplay();
} // fetchBufferBottomRow
public void mouseDragged(MouseEvent e) {
scrollViewport(e);
} // mouseDragged
private void scrollViewport(MouseEvent e) {
int _x1;
int _y1;
int _deltaX;
int _deltaY;
_x1 = e.getX();
_y1 = e.getY();
if (boundsCheck(_x1, _y1)) {
// reset painting coordinates
_deltaX = _x1 - x0;
_deltaY = _y1 - y0;
x0 = _x1;
y0 = _y1;
// because we move oppositely
currentX = currentX + _deltaX;
currentY = currentY + _deltaY;
// update viewport coords
viewportUL.x = -currentX;
viewportUL.y = -currentY;
viewportLR.x = viewportUL.x + getWidth() - 1;
viewportLR.y = viewportUL.y + getHeight() - 1;
// update coords of viewport in image coords
imgViewportUL.x -= _deltaX;
imgViewportUL.y -= _deltaY;
imgViewportLR.x = imgViewportUL.x + getWidth() - 1;
imgViewportLR.y = imgViewportUL.y + getHeight() - 1;
if (tiledViewerPanel != null) {
tiledViewerPanel.drawViewport(imgViewportUL, imgViewportLR);
}
repaint(); // using currentX, currentY as UL corner
}
} // scrollViewport
private void displayCoords(MouseEvent e) {
if (tiledViewerPanel != null) {
tiledViewerPanel.showX(e.getX() + imgViewportUL.x);
tiledViewerPanel.showY(e.getY() + imgViewportUL.y);
scaledX = tiers[levelNumToDisplay].reductionFactor *
(imgViewportUL.x + e.getX());
scaledY = tiers[levelNumToDisplay].reductionFactor *
(imgViewportUL.y + e.getY());
tiledViewerPanel.showScaledX(scaledX);
tiledViewerPanel.showScaledY(scaledY);
} // if (tiledViewerPanel != null)
} // displayCoords
public void mouseMoved(MouseEvent e) {
displayCoords(e);
} // mouseMoved
public void componentShown(java.awt.event.ComponentEvent e) {}
public void componentMoved(java.awt.event.ComponentEvent e) {}
public void componentHidden(java.awt.event.ComponentEvent e) {}
/*
* used to set the LR bounds of the viewport both at initialization
* and at any subsequent resizing of the viewport
*/
public void componentResized(ComponentEvent e) {
Component _comp;
_comp = e.getComponent();
viewportWidth = _comp.getSize().width;
viewportHeight = _comp.getSize().height;
viewportLR.x = viewportUL.x + viewportWidth - 1;
viewportLR.y = viewportUL.y + viewportHeight - 1;
imgViewportLR.x = imgViewportUL.x + viewportWidth - 1;
imgViewportLR.y = imgViewportUL.y + viewportHeight - 1;
// bring in more image if needed
while ((imgViewportLR.x > imageBuffLR.x) && !atLastColumn()) {
fetchBufferRightColumn();
}
while ((imgViewportLR.y > imageBuffLR.y) && !atLastRow()) {
fetchBufferBottomRow();
}
if (tiledViewerPanel != null) {
tiledViewerPanel.drawViewport(imgViewportUL, imgViewportLR);
}
} // componentResized
class TierData {
public int numCols; //
public int numRows; //
public int numTiles; //
public int startingTileGroup;
public int tilesInStartGroup;
public int endingTileGroup;
public int tilesInEndGroup;
public int imageWidthInPixels; // of possibly reduced image we show
public int imageHeightInPixels; // of possibly reduced image we show
public int rowNumLastTileOfImage; //
public int colNumLastTileOfImage; //
public int pixelsInLastCol; //
public int pixelsInLastRow; //
public int imgBufWidthInTiles;
public int imgBufHeightInTiles;
public int reductionFactor; // full size = 1, 1/4 size = 4, etc.
} // class TierData
} // class TiledAnnot
FILE TiledViewerPanel.java
=============================================
//package whatever;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.imageio.*;
import javax.media.jai.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.FileImageOutputStream;
class TiledViewerPanel extends JPanel
implements MouseListener, MouseMotionListener {
private static final String ICON_DIR = "./Icons/";
private static final int MOVE_UP = 0;
private static final int MOVE_DOWN = 1;
private static final int MOVE_RIGHT = 2;
private static final int MOVE_LEFT = 3;
private static final int MOVE_CENTER = 4;
private static final int IN = 0;
private static final int OUT = 1;
private static final int NAV_SIZE = 128;
private static final int SUBPANEL_W = 128;
private static final int SUBPANEL_H = 388;
private TiledViewer tiledViewer;
private JTextField percentFullsize;
private JTextField rawXField;
private JTextField rawYField;
private JTextField scaledXField;
private JTextField scaledYField;
private Box mainControlPanel;
private Locator locatorPanel;
private Dimension locPanelSize;
private String id; // id of the image that this panel displays
private String title; // image title
private Image backingBuffer;
private Graphics backingBufferG;
public TiledViewerPanel(String pUrl, int pInitialZoom) {
super();
locPanelSize = new Dimension(NAV_SIZE, NAV_SIZE);
setLayout(new BorderLayout());
initComponents(pUrl, pInitialZoom);
} // constructor
public String toString() {
return "id = "+id+", title = "+title;
}
public static TiledViewerPanel getInstance(String pUrl,
String pId,
String pTitle) {
TiledViewerPanel _retVal;
int _initialZoom;
_initialZoom = 0;
_retVal = new TiledViewerPanel(pUrl, _initialZoom);
_retVal.id = pId;
_retVal.title = pTitle;
return _retVal;
} // getInstance
public int getReductionFactor() {
return tiledViewer.getReductionFactor();
}
public Image getBackingBuffer() {
return backingBuffer;
} // getBackingBuffer
public void setBackingBuffer(Image pBuffer) {
backingBuffer = pBuffer;
} // setBackingBuffer
public Graphics getBackBufferG() {
return backingBufferG;
} // getBackBufferG
/**************** interface MouseListener methods ***********/
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
/**************** interface MouseMotionListener methods ***********/
public void mouseMoved(MouseEvent e) {}
public void mouseDragged(MouseEvent e) {}
private void initComponents(String pUrl, int pInitialZoom) {
JPanel _navSubPanel;
JPanel _zoomSubPanel;
JPanel _reductionSubPanel;
JPanel _rawCoordPanel;
JPanel _scaledCoordPanel;
JPanel _controlSubPanel;
PlanarImage _planarImage;
BufferedImage _bufferedImage;
locatorPanel = new Locator();
_navSubPanel = makeNavPanel();
_zoomSubPanel = makeZoomPanel();
_reductionSubPanel = makeReductionPanel();
_rawCoordPanel = makeRawPanel();
_scaledCoordPanel = makeScaledPanel();
_controlSubPanel = new JPanel();
_controlSubPanel.setMaximumSize(new Dimension(SUBPANEL_W, SUBPANEL_H));
_controlSubPanel.setBackground(Color.WHITE);
_controlSubPanel.setLayout(
new BoxLayout(_controlSubPanel, BoxLayout.Y_AXIS));
_controlSubPanel.add(locatorPanel);
_controlSubPanel.add(_navSubPanel);
_controlSubPanel.add(_zoomSubPanel);
_controlSubPanel.add(_reductionSubPanel);
_controlSubPanel.add(_rawCoordPanel);
_controlSubPanel.add(_scaledCoordPanel);
mainControlPanel = Box.createVerticalBox();
mainControlPanel.add(_controlSubPanel);
mainControlPanel.add(Box.createVerticalGlue());
add(mainControlPanel, BorderLayout.WEST);
tiledViewer = new TiledViewer(this);
add(tiledViewer, BorderLayout.CENTER);
tiledViewer.displayNewImage(pUrl, pInitialZoom);
} // initComponents
private void processNavButton(ActionEvent pEvent, int pHow) {
int _deltaX;
int _deltaY;
boolean _panelShift; // true => shift by panel[H | W] else tile
if (pHow == MOVE_CENTER) {
tiledViewer.recenterImage();
} else {
_panelShift = (ActionEvent.SHIFT_MASK & pEvent.getModifiers()) != 0;
switch(pHow) {
case MOVE_UP:
_deltaX = 0;
_deltaY = _panelShift ? -tiledViewer.getHeight()
: -tiledViewer.tileSizeInPixels;
break;
case MOVE_DOWN:
_deltaX = 0;
_deltaY = _panelShift ? tiledViewer.getHeight()
: tiledViewer.tileSizeInPixels;
break;
case MOVE_LEFT:
_deltaX = _panelShift ? -tiledViewer.getWidth()
: -tiledViewer.tileSizeInPixels;
_deltaY = 0;
break;
case MOVE_RIGHT:
_deltaX = _panelShift ? tiledViewer.getWidth()
: tiledViewer.tileSizeInPixels;
_deltaY = 0;
break;
default:
_deltaX = 0;
_deltaY = 0;
break;
} // switch
tiledViewer.moveULCornerRelative(_deltaX, _deltaY);
} // else
} // processNavButton
private void zoom(int pDir) {
switch(pDir) {
case IN:
if (tiledViewer.levelNumToDisplay != tiledViewer.numLevels-1) {
tiledViewer.displayNewLevel(tiledViewer.levelNumToDisplay+1);
}
break;
case OUT:
if (tiledViewer.levelNumToDisplay != 0) {
tiledViewer.displayNewLevel(tiledViewer.levelNumToDisplay-1);
}
break;
default:
break;
}
} // zoom
private JPanel makeNavPanel() {
Insets _insets;
JPanel _retVal;
JButton _upButton;
JButton _downButton;
JButton _leftButton;
JButton _rightButton;
JButton _centerButton;
ClassLoader _cl;
_retVal = new JPanel(new GridLayout(3, 3));
_insets = new Insets(0,0,0,0);
_cl = this.getClass().getClassLoader();
_upButton = new JButton(new ImageIcon(_cl.getResource("images/up.gif")));
_upButton.setMargin(_insets);
_upButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
processNavButton(e, MOVE_UP);
}
});
_downButton = new JButton(
new ImageIcon(_cl.getResource("images/down.gif")));
_downButton.setMargin(_insets);
_downButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
processNavButton(e, MOVE_DOWN);
}
});
_leftButton = new JButton(
new ImageIcon(_cl.getResource("images/left.gif")));
_leftButton.setMargin(_insets);
_leftButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
processNavButton(e, MOVE_LEFT);
}
});
_rightButton = new JButton(
new ImageIcon(_cl.getResource("images/right.gif")));
_rightButton.setMargin(_insets);
_rightButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
processNavButton(e, MOVE_RIGHT);
}
});
_centerButton = new JButton(
new ImageIcon(_cl.getResource("images/center.gif")));
_centerButton.setMargin(_insets);
_centerButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
processNavButton(e, MOVE_CENTER);
}
});
_retVal.add(new JLabel(" "));
_retVal.add(_upButton);
_retVal.add(new JLabel(" "));
_retVal.add(_leftButton);
_retVal.add(_centerButton);
_retVal.add(_rightButton);
_retVal.add(new JLabel(" "));
_retVal.add(_downButton);
_retVal.add(new JLabel(" "));
return _retVal;
} // makeNavPanel
private JPanel makeReductionPanel() {
JPanel _retVal;
_retVal = new JPanel();
_retVal.setBorder(BorderFactory.createTitledBorder(
LineBorder.createBlackLineBorder(),
"Original size %", TitledBorder.CENTER, TitledBorder.TOP));
percentFullsize = new JTextField(8);
percentFullsize.setEditable(false);
percentFullsize.setHorizontalAlignment(JTextField.CENTER);
_retVal.add(percentFullsize);
return _retVal;
} // makeReductionPanel
private JPanel makeZoomPanel() {
JPanel _retVal;
JButton _inButton;
JButton _outButton;
ClassLoader _cl;
_cl = this.getClass().getClassLoader(); // new
_retVal = new JPanel(new GridLayout(1,2));
_inButton = new JButton(
new ImageIcon(_cl.getResource("images/zoomIn.gif")));
_inButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoom(IN);
}
});
_outButton = new JButton(
new ImageIcon(_cl.getResource("images/zoomOut.gif")));
_outButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoom(OUT);
}
});
_retVal.add(_outButton);
_retVal.add(_inButton);
return _retVal;
} // makeZoomPanel
private JPanel makeRawPanel() {
JPanel _retVal;
_retVal = new JPanel(new GridLayout(2, 1));
_retVal.setBorder(BorderFactory.createTitledBorder(
LineBorder.createBlackLineBorder(),
"x,y in pixels", TitledBorder.CENTER, TitledBorder.TOP));
rawXField = new JTextField(5);
rawXField.setEditable(false);
rawXField.setHorizontalAlignment(JTextField.CENTER);
rawYField = new JTextField(5);
rawYField.setEditable(false);
rawYField.setHorizontalAlignment(JTextField.CENTER);
_retVal.add(rawXField);
_retVal.add(rawYField);
return _retVal;
} // makeRawPanel
public void showPercentFullSize(double pScale) {
String pStr;
pStr = (100.0 * pScale) +"";
if (pStr.length() > 7) {
pStr = pStr.substring(0, 7);
}
percentFullsize.setText(pStr);
}
public void showX(int pX) {
rawXField.setText("" + pX);
} // showX
public void showScaledX(double pX) {
scaledXField.setText("" + pX);
} // showScaledX
public void showY(int pY) {
rawYField.setText("" + pY);
} // showY
public void showScaledY(double pY) {
scaledYField.setText("" + pY);
} // showScaledY
private JPanel makeScaledPanel() {
JPanel _retVal;
_retVal = new JPanel(new GridLayout(2, 1));
_retVal.setBorder(BorderFactory.createTitledBorder(
LineBorder.createBlackLineBorder(),
"Scaled x,y", TitledBorder.CENTER, TitledBorder.TOP));
scaledXField = new JTextField(5);
scaledXField.setEditable(false);
scaledXField.setHorizontalAlignment(JTextField.CENTER);
scaledYField = new JTextField(5);
scaledYField.setEditable(false);
scaledYField.setHorizontalAlignment(JTextField.CENTER);
_retVal.add(scaledXField);
_retVal.add(scaledYField);
return _retVal;
} // makeScaledPanel
/*
* the public interface that enables changing an image.
*
* Pass-thru method so we don't have to expose the Locator class
*
* INPUT
* pImg: a buffered image whose contents are 0-0-0.jpg
* pImageW,
* pImageH: W & H of reduced image in pixels
* pUL: viewport upper left corner in reduced image pixel coords
* pLR: viewport lower right corner in reduced image pixel coords
*/
public void changeImage(BufferedImage pImg, int pImageW, int pImageH,
Point pUL, Point pLR) {
locatorPanel.changeImage(pImg, pImageW, pImageH, pUL, pLR);
} // initImage
public void drawViewport(Point pUL, Point pLR) {
locatorPanel.drawViewport(pUL, pLR);
} // drawViewport
public void setNewZoom(Point pUL, Point pLR, int pImageWidthInPixels) {
locatorPanel.setNewZoom(pUL, pLR, pImageWidthInPixels);
}
public static void main(String[] args) {
TiledViewerPanel _tvp;
JFrame _frame;
String _url;
int _initialZoom;
_url = "
http://brainmaps.org/HBP2/m.mulatta/RH10/RH10-highres/0804";
_initialZoom = 0;
_tvp = new TiledViewerPanel(_url, _initialZoom);
_frame = new JFrame("Tiled Viewer Test");
_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_frame.getContentPane().add(_tvp);
_frame.setSize(new Dimension(640, 480));
_frame.setVisible(true);
} // main
class Locator extends JPanel {
private Image image; // scaled such that savedImage.getWidth() <= 128
private BufferedImage savedImage; // 0-0-0.jpg from zoomify
private Rectangle vpRect; // in reduced image coords!!
// thumbnailScaleFactor is used to convert from reduced image ...
// ... coords to the displayed thumbnail coords
private double thumbnailScaleFactor;
private int desiredH; // height of the displayed thumbnail
private int desiredW; // width of the displayed thumbnail
private int xOffset;
private int yOffset;
public Locator() {
super();
setMinimumSize(locPanelSize);
setPreferredSize(locPanelSize);
setMaximumSize(locPanelSize);
vpRect = new Rectangle();
} // no-arg constructor
/*
* used to change to a NEW image
* INPUT
* pImage: the smallest (0-0-0.jpg) image in the zoomify image
* hierarchy, converted to a BufferedImage
* pReducedImageW,
* pReducedImageH: the dimensions of the reduced image in pixels
* pImgVpUL, pImgVpLRt: the viewport dimensions in reduced image
* coords: The drawn rectangle will be clipped to the
* dimensions of the scaled image
*/
public void changeImage(BufferedImage pImage, // small image: 0-0-0.jpg
int pReducedImageW, // WxH for reduced image
int pReducedImageH,
Point pImgVpUL,//viewport UL: reduced image pixels
Point pImgVpLR) {//viewport LR: "
int _savedImageH;
int _savedImageW;
savedImage = pImage;
_savedImageH = savedImage.getHeight();
_savedImageW = savedImage.getWidth();
if ((_savedImageW > NAV_SIZE) && (_savedImageH > NAV_SIZE)) {
if (_savedImageW > _savedImageH) {
desiredW = NAV_SIZE;
thumbnailScaleFactor = (double) NAV_SIZE
/ (double) pReducedImageW;
desiredH = (int) (pReducedImageH * thumbnailScaleFactor);
} else {
desiredH = NAV_SIZE;
thumbnailScaleFactor = (double) NAV_SIZE
/ (double) pReducedImageH;
desiredW = (int) (pReducedImageW * thumbnailScaleFactor);
}
} else if (_savedImageW > NAV_SIZE) { // _savedImageH <= NAV_SIZE
desiredW = NAV_SIZE;
thumbnailScaleFactor = (double) NAV_SIZE
/ (double) pReducedImageW;
desiredH = (int) ((double) pReducedImageH * thumbnailScaleFactor);
} else if (_savedImageH > NAV_SIZE) { // _savedImageW <= NAV_SIZE
desiredH = NAV_SIZE;
thumbnailScaleFactor = (double) NAV_SIZE
/ (double) pReducedImageH;
desiredW = (int) (pReducedImageW * thumbnailScaleFactor);
} else {
thumbnailScaleFactor = (double) _savedImageH
/ (double) pReducedImageH;
desiredH = _savedImageH;
desiredW = _savedImageW;
}
xOffset = (NAV_SIZE - desiredW) / 2;
yOffset = (NAV_SIZE - desiredH) / 2;
clipAndScaleVpRect(pImgVpUL, pImgVpLR);
scaleImage();
repaint();
} // changeImage
/*
* used to change the zoom level, and hence the viewport rectangle,
* on an existing image.
*
* INPUT
* pImgViewport[UL|LR]: upper left and lower right of viewport
* in reduced image pixel coordinates
* pImageWidthInPixels: width of reduced image in pixels
*
*/
public void setNewZoom(Point pImgViewportUL, Point pImgViewportLR,
int pImageWidthInPixels) {
thumbnailScaleFactor = ((double) desiredW)
/ ((double) pImageWidthInPixels);
clipAndScaleVpRect(pImgViewportUL, pImgViewportLR);
repaint();
} // setNewZoom
/*
* for drawing a viewport upon its moving or when the viewport is resized
*
* pUL, pLR: upper left and lower right coords of viewport in
* reduced image coords
*
*/
public void drawViewport(Point pUL, Point pLR) {
clipAndScaleVpRect(pUL, pLR);
repaint();
} // drawViewport
private void clipAndScaleVpRect(Point pImgViewportUL,
Point pImgViewportLR) {
int _wOrH;
if (pImgViewportUL.x < 0) {
vpRect.x = xOffset;
} else {
vpRect.x = xOffset + (int) (pImgViewportUL.x
* thumbnailScaleFactor);
}
if (pImgViewportUL.y < 0) {
vpRect.y = yOffset;
} else {
vpRect.y = yOffset + (int) (pImgViewportUL.y
* thumbnailScaleFactor);
}
// clip W or H to fit
_wOrH = (int)((pImgViewportLR.x - pImgViewportUL.x)
* thumbnailScaleFactor);
vpRect.width = (_wOrH > desiredW) ? desiredW : _wOrH;
if (vpRect.width <= 2) {
vpRect.width = 2;
} else {
vpRect.width--;
}
_wOrH = (int) ((pImgViewportLR.y - pImgViewportUL.y)
* thumbnailScaleFactor);
vpRect.height = (_wOrH > desiredH) ? desiredH : _wOrH;
if (vpRect.height <= 2) {
vpRect.height = 2;
} else {
vpRect.height--;
}
} // clipAndScaleVpRect
private void scaleImage() {
MediaTracker _tracker;
_tracker = new MediaTracker(this);
image = savedImage.getScaledInstance(desiredW, desiredH,
Image.SCALE_SMOOTH);
_tracker.addImage(image, 0); // assign id = 0 to this image
try {
_tracker.waitForID(0);
} catch (InterruptedException e) {
System.err.println("scaleImage(): image scaling interrupted!");
}
} // scaleImage
public void paint(Graphics pG) {
pG.clearRect(0, 0, NAV_SIZE, NAV_SIZE);
pG.drawImage(image, xOffset, yOffset, null);
pG.drawRect(vpRect.x, vpRect.y, vpRect.width, vpRect.height);
} // paint
} // class Locator
} // class TiledViewerPanel
--
Shawn Mikula, Ph.D.,
Postdoctoral Scholar
Center for Neuroscience
University of California-Davis,
1544 Newton Court,
Davis, CA 95618,
Phone: 530-754-9209
Fax: 530-754-9136
mail:
[hidden email]
web:
http://brainmaps.org