Example #1
0
  private void writeImage(
      final File file,
      byte[] data,
      final int xsize,
      final int ysize,
      final int zsize,
      final boolean yflip)
      throws IOException {
    // Input data is in RGBRGBRGB or RGBARGBARGBA format; first unswizzle it
    final byte[] tmpData = new byte[xsize * ysize * zsize];
    int dest = 0;
    for (int i = 0; i < zsize; i++) {
      for (int j = i; j < (xsize * ysize * zsize); j += zsize) {
        tmpData[dest++] = data[j];
      }
    }
    data = tmpData;

    // requires: DATA must be an array of size XSIZE * YSIZE * ZSIZE,
    //           indexed in the following manner:
    //             data[0]    ...data[xsize-1] == first row of first channel
    //             data[xsize]...data[2*xsize-1]   == second row of first channel
    //         ... data[(ysize - 1) * xsize]...data[(ysize * xsize) - 1] ==
    //                                            last row of first channel
    //           Later channels follow the same format.
    //           *** NOTE that "first row" is defined by the BOTTOM ROW of
    //           the image. That is, the origin is in the lower left corner.
    // effects: writes out an SGI image to FILE, RLE-compressed, INCLUDING
    //          header, of dimensions (xsize, ysize, zsize), and containing
    //          the data in DATA. If YFLIP is set, outputs the data in DATA
    //          in reverse order vertically (equivalent to a flip about the
    //          x axis).

    // Build the offset tables
    final int[] starttab = new int[ysize * zsize];
    final int[] lengthtab = new int[ysize * zsize];

    // Temporary buffer for holding RLE data.
    // Note that this makes the assumption that RLE-compressed data will
    // never exceed twice the size of the input data.
    // There are surely formal proofs about how big the RLE buffer should
    // be, as well as what the optimal look-ahead size is (i.e. don't switch
    // copy/repeat modes for less than N repeats). However, I'm going from
    // empirical evidence here; the break-even point seems to be a look-
    // ahead of 3. (That is, if the three values following this one are all
    // the same as the current value, switch to repeat mode.)
    final int lookahead = 3;
    final byte[] rlebuf = new byte[2 * xsize * ysize * zsize];

    int cur_loc = 0; // current offset location.
    int ptr = 0;
    int total_size = 0;
    int ystart = 0;
    int yincr = 1;
    int yend = ysize;

    if (yflip) {
      ystart = ysize - 1;
      yend = -1;
      yincr = -1;
    }

    final boolean DEBUG = false;

    for (int z = 0; z < zsize; z++) {
      for (int y = ystart; y != yend; y += yincr) {
        // RLE-compress each row.

        int x = 0;
        byte count = 0;
        boolean repeat_mode = false;
        boolean should_switch = false;
        final int start_ptr = ptr;
        int num_ptr = ptr++;
        byte repeat_val = 0;

        while (x < xsize) {
          // see if we should switch modes
          should_switch = false;
          if (repeat_mode) {
            if (imgref(data, x, y, z, xsize, ysize, zsize) != repeat_val) {
              should_switch = true;
            }
          } else {
            // look ahead to see if we should switch to repeat mode.
            // stay within the scanline for the lookahead
            if ((x + lookahead) < xsize) {
              should_switch = true;
              for (int i = 1; i <= lookahead; i++) {
                if (DEBUG)
                  System.err.println(
                      "left side was "
                          + ((int) imgref(data, x, y, z, xsize, ysize, zsize))
                          + ", right side was "
                          + (int) imgref(data, x + i, y, z, xsize, ysize, zsize));

                if (imgref(data, x, y, z, xsize, ysize, zsize)
                    != imgref(data, x + i, y, z, xsize, ysize, zsize)) should_switch = false;
              }
            }
          }

          if (should_switch || (count == 127)) {
            // update the number of elements we repeated/copied
            if (x > 0) {
              if (repeat_mode) rlebuf[num_ptr] = count;
              else rlebuf[num_ptr] = (byte) (count | 0x80);
            }
            // perform mode switch if necessary; output repeat_val if
            // switching FROM repeat mode, and set it if switching
            // TO repeat mode.
            if (repeat_mode) {
              if (should_switch) repeat_mode = false;
              rlebuf[ptr++] = repeat_val;
            } else {
              if (should_switch) repeat_mode = true;
              repeat_val = imgref(data, x, y, z, xsize, ysize, zsize);
            }

            if (x > 0) {
              // reset the number pointer
              num_ptr = ptr++;
              // reset number of bytes copied
              count = 0;
            }
          }

          // if not in repeat mode, copy element to ptr
          if (!repeat_mode) {
            rlebuf[ptr++] = imgref(data, x, y, z, xsize, ysize, zsize);
          }
          count++;

          if (x == xsize - 1) {
            // Need to store the number of pixels we copied/repeated.
            if (repeat_mode) {
              rlebuf[num_ptr] = count;
              // If we ended the row in repeat mode, store the
              // repeated value
              rlebuf[ptr++] = repeat_val;
            } else rlebuf[num_ptr] = (byte) (count | 0x80);

            // output zero counter for the last value in the row
            rlebuf[ptr++] = 0;
          }

          x++;
        }
        // output this row's length into the length table
        final int rowlen = ptr - start_ptr;
        if (yflip) lengthtab[ysize * z + (ysize - y - 1)] = rowlen;
        else lengthtab[ysize * z + y] = rowlen;
        // add to the start table, and update the current offset
        if (yflip) starttab[ysize * z + (ysize - y - 1)] = cur_loc;
        else starttab[ysize * z + y] = cur_loc;
        cur_loc += rowlen;
      }
    }

    // Now we have the offset tables computed, as well as the RLE data.
    // Output this information to the file.
    total_size = ptr;

    if (DEBUG) System.err.println("total_size was " + total_size);

    final DataOutputStream stream =
        new DataOutputStream(new BufferedOutputStream(IOUtil.getFileOutputStream(file, true)));

    writeHeader(stream, xsize, ysize, zsize, true);

    final int SIZEOF_INT = 4;
    for (int i = 0; i < (ysize * zsize); i++)
      stream.writeInt(starttab[i] + 512 + (2 * ysize * zsize * SIZEOF_INT));
    for (int i = 0; i < (ysize * zsize); i++) stream.writeInt(lengthtab[i]);
    for (int i = 0; i < total_size; i++) stream.write(rlebuf[i]);

    stream.close();
  }
Example #2
0
  private void writeHeader(
      final DataOutputStream stream,
      final int xsize,
      final int ysize,
      final int zsize,
      final boolean rle)
      throws IOException {
    // effects: outputs the 512-byte IRIS RGB header to STREAM, using xsize,
    //          ysize, and depth as the dimensions of the image. NOTE that
    //          the following defaults are used:
    //              STORAGE = 1     (storage format = RLE)
    //              BPC = 1         (# bytes/channel)
    //              DIMENSION = 3
    //              PIXMIN = 0
    //              PIXMAX = 255
    //              IMAGENAME = <80 nulls>
    //              COLORMAP = 0
    //          See ftp://ftp.sgi.com/pub/sgi/SGIIMAGESPEC for more details.

    // write out MAGIC, STORAGE, BPC
    stream.writeShort(474);
    stream.write((rle ? 1 : 0));
    stream.write(1);

    // write out DIMENSION
    stream.writeShort(3);

    // write XSIZE, YSIZE, ZSIZE
    stream.writeShort(xsize);
    stream.writeShort(ysize);
    stream.writeShort(zsize);

    // write PIXMIN, PIXMAX
    stream.writeInt(0);
    stream.writeInt(255);

    // write DUMMY
    stream.writeInt(0);

    // write IMAGENAME
    for (int i = 0; i < 80; i++) stream.write(0);

    // write COLORMAP
    stream.writeInt(0);

    // write DUMMY (404 bytes)
    for (int i = 0; i < 404; i++) stream.write(0);
  }