// cf. Steven Eker. Faster "Pixel-Perfect" Line Clipping. Graphics Gems V:
  // 314-322.
  // - Pixels are not missed at the ends of a clipped segment.
  // - Visible pixels are the same as if there was no clipping.
  private boolean clipLine(Rectangle r, final Line sl, Line dl) {
    int code1 = 0, code2 = 0;

    dl.x0 = sl.x0;
    dl.y0 = sl.y0;
    dl.x1 = sl.x1;
    dl.y1 = sl.y1;

    if (sl.x0 < r.xmin) code1 |= CLIP_LEFT;
    if (sl.x0 > r.xmax) code1 |= CLIP_RIGHT;
    if (sl.y0 < r.ymin) code1 |= CLIP_BOTTOM;
    if (sl.y0 > r.ymax) code1 |= CLIP_TOP;

    if (sl.x1 < r.xmin) code2 |= CLIP_LEFT;
    if (sl.x1 > r.xmax) code2 |= CLIP_RIGHT;
    if (sl.y1 < r.ymin) code2 |= CLIP_BOTTOM;
    if (sl.y1 > r.ymax) code2 |= CLIP_TOP;

    if ((code1 | code2) == 0) return false; // Trivial accept.
    if ((code1 & code2) != 0) return true; // Trivial reject.

    // Clip first end point.
    if (code1 != 0) if (clipEndPoint(r, sl, dl, code1, false)) return true;
    // Clip second end point.
    if (code2 != 0) if (clipEndPoint(r, sl, dl, code2, true)) return true;

    return false;
  }
  private boolean clipEndPoint(Rectangle r, final Line sl, Line dl, int code, boolean swap) {
    // Rounding mode: 0=round toward 0, 1=round toward +infinity.
    int x, y, dx, dy, sx = 1, sy = 1, roundX = 0, roundY = 0;
    if (swap) {
      x = sl.x1;
      y = sl.y1;
      dx = sl.x0 - sl.x1;
      dy = sl.y0 - sl.y1;
    } else {
      x = sl.x0;
      y = sl.y0;
      dx = sl.x1 - sl.x0;
      dy = sl.y1 - sl.y0;
    }
    if (dx < 0) {
      dx = -dx;
      sx = -1;
      roundX ^= 1;
    }
    if (dy < 0) {
      dy = -dy;
      sy = -1;
      roundY ^= 1;
    }

    int dx2 = dx << 1, dy2 = dy << 1, t0 = 0, t1 = 0;
    int xc = x, yc = y;

    if ((code & CLIP_LEFT) != 0) {
      t0 = dy2 * (r.xmin - x);
      xc = r.xmin;
    }
    if ((code & CLIP_RIGHT) != 0) {
      t0 = dy2 * (x - r.xmax);
      xc = r.xmax;
    }
    if ((code & CLIP_BOTTOM) != 0) {
      t1 = dx2 * (r.ymin - y);
      yc = r.ymin;
    }
    if ((code & CLIP_TOP) != 0) {
      t1 = dx2 * (y - r.ymax);
      yc = r.ymax;
    }

    if (CLIP_ZONE_FOR_CODE[code] == CLIP_ZONE_DIAGONALS) {
      // Find which edge clips the line first and remove a clip flag.
      if (dx >= dy) code &= ((t0 - t1 + dx - (roundX ^ 1)) < 0) ? ~CLIP_HORIZONTAL : ~CLIP_VERTICAL;
      else code &= ((t1 - t0 + dy - (roundY ^ 1)) < 0) ? ~CLIP_VERTICAL : ~CLIP_HORIZONTAL;
    }

    if ((code & CLIP_HORIZONTAL) != 0) {
      // Clip to left or right edge.
      t0 = (dx >= dy) ? (t0 + dx - (roundX ^ 1)) / dx2 : (t0 - dy + dx2 - roundY) / dx2;
      yc = (sy < 0) ? (y - t0) : (y + t0);
      if ((yc < r.ymin) || (yc > r.ymax)) return true;
    } else {
      // Clip to bottom or top edge.
      t1 = (dx >= dy) ? (t1 - dx + dy2 - roundX) / dy2 : (t1 + dy - (roundY ^ 1)) / dy2;
      xc = (sx < 0) ? (x - t1) : (x + t1);
      if ((xc < r.xmin) || (xc > r.xmax)) return true;
    }

    if (swap) {
      dl.x1 = xc;
      dl.y1 = yc;
    } else {
      dl.x0 = xc;
      dl.y0 = yc;
    }

    return false;
  }
  private void drawLine(int x0, int y0, int x1, int y1, boolean lastPixelFlag) {
    if (y0 == y1) {
      drawHLine(x0, x1, y0);
      return;
    }
    if (x0 == x1) {
      drawVLine(y0, y1, x0);
      return;
    }

    // Clip.
    Line sl = srcLine, dl = dstLine;

    // sl.x0=x0-translationX; sl.x1=x1-translationX; sl.y0=y0-translationY;
    // sl.y1=y1-translationY;
    sl.x0 = x0 + transX;
    sl.x1 = x1 + transX;
    sl.y0 = y0 + transY;
    sl.y1 = y1 + transY;

    if (clipLine(clipRectangle, sl, dl)) return;

    // Draw.
    {
      int x = dl.x0, y = dl.y0, w = dl.x1 - dl.x0, h = dl.y1 - dl.y0;
      int ex = dl.x0 - sl.x0, ey = dl.y0 - sl.y0;
      int dx = sl.x1 - sl.x0, dy = sl.y1 - sl.y0;
      int sx = 1, sy = 1;
      if (dx < 0) {
        dx = -dx;
        sx = -sx;
        ex = -ex;
        w = -w;
      }
      if (dy < 0) {
        dy = -dy;
        sy = -sy;
        ey = -ey;
        h = -h;
      }

      if (dx >= dy) {
        int decision = (ex + ex + 2) * dy - (ey + ey + 1) * dx;
        if (sx > 0) decision--; // Modify the decision variable for generating
        // the same pattern from left to right and right
        // to left.
        dx += dx;
        dy += dy;
        if (lastPixelFlag) w++;
        while (w > 0) {
          drawSpan(x, y, 1);
          if (decision >= 0) {
            decision -= dx;
            y += sy;
          }
          decision += dy;
          x += sx;
          w--;
        }
      } else {
        int decision = (ey + ey + 2) * dx - (ex + ex + 1) * dy;
        if (sy > 0) decision--; // Modify the decision variable for generating
        // the same pattern from bottom to top and top
        // to bottom.
        dx += dx;
        dy += dy;
        if (lastPixelFlag) h++;
        while (h > 0) {
          drawSpan(x, y, 1);
          if (decision >= 0) {
            decision -= dy;
            x += sx;
          }
          decision += dx;
          y += sy;
          h--;
        }
      }
    }
  }