private void writePixels(String chunk, byte[] stream, int x, int y, int width, int height)
      throws FormatException, IOException {
    MetadataRetrieve r = getMetadataRetrieve();
    int sizeC = getSamplesPerPixel();
    String type = r.getPixelsType(series).toString();
    int pixelType = FormatTools.pixelTypeFromString(type);
    boolean signed = FormatTools.isSigned(pixelType);

    if (!isFullPlane(x, y, width, height)) {
      throw new FormatException("APNGWriter does not support writing tiles.");
    }

    ByteArrayOutputStream s = new ByteArrayOutputStream();
    s.write(chunk.getBytes());
    if (chunk.equals("fdAT")) {
      s.write(DataTools.intToBytes(nextSequenceNumber++, false));
    }
    DeflaterOutputStream deflater = new DeflaterOutputStream(s);
    int planeSize = stream.length / sizeC;
    int rowLen = stream.length / height;
    int bytesPerPixel = stream.length / (width * height * sizeC);
    byte[] rowBuf = new byte[rowLen];
    for (int i = 0; i < height; i++) {
      deflater.write(0);
      if (interleaved) {
        if (littleEndian) {
          for (int col = 0; col < width * sizeC; col++) {
            int offset = (i * sizeC * width + col) * bytesPerPixel;
            int pixel = DataTools.bytesToInt(stream, offset, bytesPerPixel, littleEndian);
            DataTools.unpackBytes(pixel, rowBuf, col * bytesPerPixel, bytesPerPixel, false);
          }
        } else System.arraycopy(stream, i * rowLen, rowBuf, 0, rowLen);
      } else {
        int max = (int) Math.pow(2, bytesPerPixel * 8 - 1);
        for (int col = 0; col < width; col++) {
          for (int c = 0; c < sizeC; c++) {
            int offset = c * planeSize + (i * width + col) * bytesPerPixel;
            int pixel = DataTools.bytesToInt(stream, offset, bytesPerPixel, littleEndian);
            if (signed) {
              if (pixel < max) pixel += max;
              else pixel -= max;
            }
            int output = (col * sizeC + c) * bytesPerPixel;
            DataTools.unpackBytes(pixel, rowBuf, output, bytesPerPixel, false);
          }
        }
      }
      deflater.write(rowBuf);
    }
    deflater.finish();
    byte[] b = s.toByteArray();

    // write chunk length
    out.writeInt(b.length - 4);
    out.write(b);

    // write checksum
    out.writeInt(crc(b));
  }
  /* @see loci.formats.IFormatHandler#setId(String) */
  public void setId(String id) throws FormatException, IOException {
    super.setId(id);

    if (out.length() == 0) {
      MetadataRetrieve r = getMetadataRetrieve();
      int width = r.getPixelsSizeX(series).getValue().intValue();
      int height = r.getPixelsSizeY(series).getValue().intValue();
      int bytesPerPixel = FormatTools.getBytesPerPixel(r.getPixelsType(series).toString());
      int nChannels = getSamplesPerPixel();
      boolean indexed = getColorModel() != null && (getColorModel() instanceof IndexColorModel);
      littleEndian = !r.getPixelsBinDataBigEndian(series, 0);

      // write 8-byte PNG signature
      out.write(PNG_SIG);

      // write IHDR chunk

      out.writeInt(13);
      byte[] b = new byte[17];
      b[0] = 'I';
      b[1] = 'H';
      b[2] = 'D';
      b[3] = 'R';

      DataTools.unpackBytes(width, b, 4, 4, false);
      DataTools.unpackBytes(height, b, 8, 4, false);

      b[12] = (byte) (bytesPerPixel * 8);
      if (indexed) b[13] = (byte) 3;
      else if (nChannels == 1) b[13] = (byte) 0;
      else if (nChannels == 2) b[13] = (byte) 4;
      else if (nChannels == 3) b[13] = (byte) 2;
      else if (nChannels == 4) b[13] = (byte) 6;
      b[14] = (byte) 0;
      b[15] = (byte) 0;
      b[16] = (byte) 0;

      out.write(b);
      out.writeInt(crc(b));

      // write acTL chunk

      out.writeInt(8);
      out.writeBytes("acTL");
      numFramesPointer = out.getFilePointer();
      out.writeInt(0);
      out.writeInt(0);
      out.writeInt(0); // save a place for the CRC
    }
  }
  private void writeFooter() throws IOException {
    // write IEND chunk
    out.writeInt(0);
    out.writeBytes("IEND");
    out.writeInt(crc("IEND".getBytes()));

    // update frame count
    out.seek(numFramesPointer);
    out.writeInt(numFrames);
    out.skipBytes(4);
    byte[] b = new byte[12];
    b[0] = 'a';
    b[1] = 'c';
    b[2] = 'T';
    b[3] = 'L';
    DataTools.unpackBytes(numFrames, b, 4, 4, false);
    DataTools.unpackBytes(0, b, 8, 4, false);
    out.writeInt(crc(b));
  }
  private void writeFCTL(int width, int height) throws IOException {
    out.writeInt(26);
    byte[] b = new byte[30];
    b[0] = 'f';
    b[1] = 'c';
    b[2] = 'T';
    b[3] = 'L';

    DataTools.unpackBytes(nextSequenceNumber++, b, 4, 4, false);
    DataTools.unpackBytes(width, b, 8, 4, false);
    DataTools.unpackBytes(height, b, 12, 4, false);
    DataTools.unpackBytes(0, b, 16, 4, false);
    DataTools.unpackBytes(0, b, 20, 4, false);
    DataTools.unpackBytes(1, b, 24, 2, false);
    DataTools.unpackBytes(fps, b, 26, 2, false);
    b[28] = (byte) 1;
    b[29] = (byte) 0;

    out.write(b);
    out.writeInt(crc(b));
  }