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.tgz to 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 |
Free forum by Nabble | Edit this page |