boardcanvas.java
来自「ErGo是一个很早的Java通用围棋服务器(IGS/NNGS)客户端程序。有全部」· Java 代码 · 共 1,029 行 · 第 1/3 页
JAVA
1,029 行
whiteShadowStone = StoneMaker.makeStoneImage(Move.WHITE, true, cellSize,
stoneStyle, backcoldark, adjustwhite);
blackShadowStone = StoneMaker.makeStoneImage(Move.BLACK, true, cellSize,
stoneStyle, backcollight, 0.0);
hoshi = new RawImage(cellSize, cellSize);
circle.fillcirc(hoshi, 0xff000000, 4);
hoshiIm = createImage(hoshi.mis());
}
private void drawGrid (Graphics g) {
g.setColor(getForeground());
int top = yoffset + cellSize/2;
int bottom = top + (gameSize - 1) * cellSize;
int left = xoffset + cellSize/2;
int right = left + (gameSize - 1) * cellSize;
for (int i = 0; i < gameSize; i++) {
int y = top + i * cellSize;
int x = left + i * cellSize;
g.drawLine(left, y, right, y);
g.drawLine(x, top, x, bottom);
}
// draw all the star points
PositionVector hoshis = starpoints.points();
if (hoshis != null)
for (int i = 0; i < hoshis.size(); i++) {
drawStarPoint(g, hoshis.elementAt(i));
}
}
private void drawStarPoint (Graphics g, Position pos) {
// System.out.println("Hoshis out!");
drawStarPoint(g, pos.row, pos.column);
}
private void drawStarPoint (Graphics g, int row, int col) {
int left = coltox(col);
int top = rowtoy(row);
g.drawImage(hoshiIm, left, top, this);
}
private void drawStone (Graphics g, Position pos, int color) {
drawStone(g, pos.row, pos.column, color);
}
private void drawStone (Graphics g, int row, int col, int color) {
// long time = System.currentTimeMillis();
int left = coltox(col);
int top = rowtoy(row);
// Debug.println("dS: "+color+" at "+row+","+col);
switch (color) {
case Move.WHITE:
makeEmptyImage(row, col);
ImArith.api(tempblit, whiteStone);
blitIt(row, col);
break;
case Move.BLACK:
makeEmptyImage(row, col);
ImArith.api(tempblit, blackStone);
blitIt(row, col);
break;
case Move.WHITESHADOW:
makeEmptyImage(row, col);
ImArith.api(tempblit, whiteShadowStone);
blitIt(row, col);
break;
case Move.BLACKSHADOW:
makeEmptyImage(row, col);
ImArith.api(tempblit, blackShadowStone);
blitIt(row, col);
break;
case Move.WHITETERRITORY:
case Move.BLACKTERRITORY:
// Now that we have shadow stones we must call eraseStone here
// to make sure that the entire shadow stone gets erased.
// This is a temporary thing. Shadow stones should eventually
// only highlight non-empty positions when in (server?) scoring mode (in
// case the user wants to remove the group), in which case this
// call won't be necessary.
eraseStone(g, row, col);
int inset = cellSize / 4;
int half = inset * 2;
g.setColor(codeToColor(color));
g.fillRect(left + inset, top + inset, half, half);
g.setColor(Color.darkGray);
g.drawRect(left + inset, top + inset, half, half);
break;
case Move.EMPTY:
eraseStone(g, row, col);
break;
}
// System.out.println("Drawn in "+ (System.currentTimeMillis() - time));
// System.out.println(tempblit);
}
private void eraseStone (Graphics g, int row, int col) {
makeEmptyImage(row, col);
blitIt(row,col);
}
private void eraseStone (Graphics g, Position pos) {
eraseStone(g, pos.row, pos.column);
}
private void highlightMove (Graphics g, StoneMove m) {
double sin45 = 0.707; // = cos45
int off = (cellSize%2 == 0)?1:0;
// System.out.println(off);
int d = (int) ((cellSize - 7) * sin45)/2;
int cx = coltox(m.position().column) + cellSize/2;
int cy = rowtoy(m.position().row) + cellSize/2;
g.setColor(codeToColor((m.color() == Move.WHITE) ? Move.BLACK : Move.WHITE));
g.drawLine(cx - d - off, cy - d - off, cx + d, cy + d);
g.drawLine(cx + d, cy - d - off, cx - d - off, cy + d);
// previouslyHighlightedMove = m;
}
private void drawLetter (Graphics g, Position pos, char[] c) {
FontMetrics metrics = g.getFontMetrics(getFont());
int cwidth = metrics.charsWidth(c, 0, 1);
int loffset = Math.max(0, cellSize - cwidth) / 2;
int left = coltox(pos.column) + loffset;
int cheight = metrics.getHeight();
int toffset = Math.max(0, cellSize - cheight) / 2;
int top = rowtoy(pos.row) + toffset;
int baseline = top + metrics.getAscent();
Rectangle r = g.getClipBounds();
g.setClip(left, top, cwidth, cheight);
makeEmptyImage(pos.row, pos.column, true);
blitIt(pos.row, pos.column);
if (r == null)
g.setClip(0, 0, width, height);
else
g.setClip(r.x, r.y, r.width, r.height);
if (boardStyle == BOARD_BASIC || oodboard == null)
g.setColor(getForeground());
else
g.setColor(Color.white);
g.drawChars(c, 0, 1, left, baseline);
}
/**************************
MID level drawing routines - these draw higher-level constructs
**************************
/*
* Move highlighting.
* Displays an X on the most recent move
*/
PositionVector decorations = new PositionVector();
private void highlightCurrentMove (Graphics g) {
// unhighlightPreviousMove(g);
// !!!!! Remaining reference to gwin.
if (currentMove instanceof StoneMove) {
StoneMove sm=(StoneMove)currentMove;
highlightMove(g, sm);
invalidateInternal(sm.position().row, sm.position().column);
decorations.addElement(sm.position());
}
}
/*
* Draw a letter for each variation of the current move.
*/
private void drawVariations (Graphics g) {
gwin.updateVariationsMenu();
if (currentMove != null && g != null) {
FontMetrics metrics = g.getFontMetrics(getFont());
for(int i = -1; ; i++) {
Move m = currentMove.variationAt(i);
if (m == null)
break;
else {
PositionVector placed = m.placedPositions(gameSize);
// c is an array because there is only drawChars()
char[] c = { (char) (i + 1 + (int) 'a') };
if (placed != null) {
for (int j = 0; j < placed.size(); j++) {
Position pos = placed.elementAt(j);
invalidateInternal(pos.row, pos.column);
// remember to invalidate these decorations when the current
// move changes...AMB
decorations.addElement(pos);
drawLetter(g, pos, c);
}
} // end if any positions for this variation.
} // end if variation here.
} // end for possible variations
} // end if there is a current move.
}
/**
* Draw the entire board, up to the current move.
*/
// !!!!! Dependence: gwin.gameResult.
private void drawAll (Graphics g) {
int style = currentBoardStyle(); // synced.
if (style == BOARD_BASIC) {
g.setColor(boardColor);
g.fillRect(0, 0, width, height);
}
else {
// This shouldn't happen often - can afford to throw away image.
Image i = createImage(oodboard.mis());
g.drawImage(i, 0, 0, null);
i.flush();
}
drawGrid(g);
boolean displayAny = (currentMove instanceof GameResultMove);
synchronized (ss) {
for (int i = 0; i < gameSize; ++i) {
for (int j = 0; j < gameSize; ++j)
if (displayAny)
reflect(i, j, ss.readAnyStone(i, j));
else {
int stone = ss.readStone(i, j);
if (stone != Move.EMPTY)
reflect(i, j, stone);
}
}
}
decorateStone(g);
}
private void decorateStone (Graphics g) {
if (showVariations)
drawVariations(g);
highlightCurrentMove(g);
}
/****************************************
EXTERNAL level - manipulations meaningful to the outside world!
****************************************/
/** Update the offscreen bitmap, and the internal invalidation bounding-box....
*/
public void reflect (int row, int col, int color) {
drawStone(offg, row, col, color);
invalidateInternal(row, col);
}
public void setKoPosition (Position p) {
koPosition = p;
}
/** Reflect Immediate Single stone at position... but note that
* slightly extra duty - current extra call is from mouseCommon, for
* shadow stones - we want to replace decorations if we go over the
* top of them.
*/
private void reflectis (Position p) {
int color = ss.readAnyStone(p.row, p.column);
drawStone(offg, p.row, p.column, color);
invalidateInternal(p.row, p.column);
if (decorations.isMember(p))
decorateStone(offg);
updateInternal();
}
// Top left and bottom right positions of bounding box that needs
// to be redisplayed. Note that since board positions (Position)
// use a 1st quadrant coordinate system and the AWT uses the 4th
// quadrant, and these are Positions, I changed the logic to use
// a 1st coordinate system. The only place the transformation is
// done now is in the call to repaint() in updateInternal(). -sigue
Position boundTL = new Position(-1, -1); // bounding box for internal
Position boundBR = new Position(-1, -1); // invalidation.......
private void invalidateInternal (int row, int col) {
if (boundTL.row == -1) {
boundTL.row = boundBR.row = row;
boundTL.column = boundBR.column = col;
}
else {
// Note Positions use a 1st quadrant system. Higher row number
// means higher position on screen.
boundTL.row = Math.max(boundTL.row, row);
boundBR.row = Math.min(boundBR.row, row);
boundTL.column = Math.min(boundTL.column, col);
boundBR.column = Math.max(boundBR.column, col);
}
}
// This is an experiment - do updates synchonously. AMB at 0.14
public void updateInternal () {
if (boundTL.row != -1) {
if (staticg != null) {
staticg.setClip(coltox(boundTL.column), rowtoy(boundTL.row),
coltox(boundBR.column + 1) - coltox(boundTL.column),
rowtoy(boundBR.row) - rowtoy(boundTL.row + 1));
update(staticg);
}
// Tell invalidateInternal() to start a new bounding box.
boundTL.row = -1;
}
}
/** Moves the decorations when the active move has changed. */
public void noteMove (Move currentMove) {
this.currentMove = currentMove;
for (int i = decorations.size() - 1; i >= 0; --i) {
int row = decorations.elementAt(i).row;
int col = decorations.elementAt(i).column;
// logic error now removed - AMB.
reflect(row, col, ss.readAnyStone(row, col));
updateInternal();
decorations.removeElementAt(i);
}
decorateStone(offg);
}
/*
* When a component wants to redraw part or all of itself it should
* call repaint(), possibly specifying the region to be repainted.
* This tells the AWT to schedule a call to update(), which by default
* just clears the component and calls paint(). When the component
* has just been exposed the AWT calls paint() directly.
* Updates may be required through two causes:
* Either exposing of part of the component (external), or through change
* of state of the board (internal). We wish to respond to external events
* as quickly as possible - in particular, in the case of observing a game,
* there may be some delay before the stoneArray is ready for public
* consumption during which a couple of externals may arrive from an
* impatient user....
* *Game* will be in charge of caching these changes - and whenever
* an internal validation event occurs, we assume that it will be as soon as
* possible after the offscreen bitmap starts to be scrawled to.....
*/
/** This flag is set to true if a complete regeneration of the offscreen
* is required. For example, when the board style changes, or the wood
* background image becomes ready.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?