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 + -
显示快捷键?