void decodePass(int xOffset, int yOffset, int xStep, int yStep, int passWidth, int passHeight) {
    if ((passWidth == 0) || (passHeight == 0)) {
      return;
    }

    int bytesPerRow = (inputBands * passWidth * bitDepth + 7) / 8;
    byte[] curr = new byte[bytesPerRow];
    byte[] prior = new byte[bytesPerRow];

    // Decode the (sub)image row-by-row
    int srcY, dstY;
    for (srcY = 0, dstY = yOffset; srcY < passHeight; srcY++, dstY += yStep) {
      // Read the filter type byte and a row of data
      int filter = 0;
      try {
        filter = dataStream.read();
        dataStream.readFully(curr, 0, bytesPerRow);
      } catch (Exception e) {
        // empty on purpose
      }

      switch (filter) {
        case PNG_FILTER_NONE:
          break;
        case PNG_FILTER_SUB:
          decodeSubFilter(curr, bytesPerRow, bytesPerPixel);
          break;
        case PNG_FILTER_UP:
          decodeUpFilter(curr, prior, bytesPerRow);
          break;
        case PNG_FILTER_AVERAGE:
          decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel);
          break;
        case PNG_FILTER_PAETH:
          decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel);
          break;
        default:
          // Error -- uknown filter type
          throw new RuntimeException("PNG filter unknown.");
      }

      processPixels(curr, xOffset, xStep, dstY, passWidth);

      // Swap curr and prior
      byte[] tmp = prior;
      prior = curr;
      curr = tmp;
    }
  }