/** * Constructs a random access stream around the given handle, and with the associated file path. */ public RandomAccessInputStream( final Context context, final IRandomAccess handle, final String file) throws IOException { scifio = new SCIFIO(context); log = scifio.log(); if (log.isTrace()) { log.trace("RandomAccessInputStream " + hashCode() + " OPEN"); } this.file = file; raf = handle; raf.setOrder(ByteOrder.BIG_ENDIAN); seek(0); length = -1; }
/** * The CodecOptions parameter should have the following fields set: {@link CodecOptions#width * width} {@link CodecOptions#height height} * * @see Codec#decompress(RandomAccessInputStream, CodecOptions) */ @Override public byte[] decompress(final RandomAccessInputStream in, CodecOptions options) throws FormatException, IOException { if (in == null) throw new IllegalArgumentException("No data to decompress."); if (options == null) options = CodecOptions.getDefaultOptions(); in.skipBytes(8); final int plane = options.width * options.height; stride = options.width; final int rowInc = stride - 4; short opcode; int nBlocks; int colorA = 0, colorB; final int[] color4 = new int[4]; int index, idx; int ta, tb; int blockPtr = 0; rowPtr = pixelPtr = 0; int pixelX, pixelY; final int[] pixels = new int[plane]; final byte[] rtn = new byte[plane * 3]; while (in.read() != (byte) 0xe1) { /* Read to block metadata */ } in.skipBytes(3); // totalBlocks = ((options.width + 3) / 4) * ((options.height + 3) / 4); while (in.getFilePointer() + 2 < in.length()) { opcode = in.readByte(); nBlocks = (opcode & 0x1f) + 1; if ((opcode & 0x80) == 0) { if (in.getFilePointer() >= in.length()) break; colorA = (opcode << 8) | in.read(); opcode = 0; if (in.getFilePointer() >= in.length()) break; if ((in.read() & 0x80) != 0) { opcode = 0x20; nBlocks = 1; } in.seek(in.getFilePointer() - 1); } switch (opcode & 0xe0) { case 0x80: while (nBlocks-- > 0) { updateBlock(options.width); } break; case 0xa0: if (in.getFilePointer() + 2 >= in.length()) break; colorA = in.readShort(); while (nBlocks-- > 0) { blockPtr = rowPtr + pixelPtr; for (pixelY = 0; pixelY < 4; pixelY++) { for (pixelX = 0; pixelX < 4; pixelX++) { if (blockPtr >= pixels.length) break; pixels[blockPtr] = colorA; final short s = (short) (pixels[blockPtr] & 0x7fff); unpack(s, rtn, blockPtr, pixels.length); blockPtr++; } blockPtr += rowInc; } updateBlock(options.width); } break; case 0xc0: case 0x20: if (in.getFilePointer() + 2 >= in.length()) break; if ((opcode & 0xe0) == 0xc0) { colorA = in.readShort(); } colorB = in.readShort(); color4[0] = colorB; color4[1] = 0; color4[2] = 0; color4[3] = colorA; ta = (colorA >> 10) & 0x1f; tb = (colorB >> 10) & 0x1f; color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10; color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10; ta = (colorA >> 5) & 0x1f; tb = (colorB >> 5) & 0x1f; color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5; color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5; ta = colorA & 0x1f; tb = colorB & 0x1f; color4[1] |= (11 * ta + 21 * tb) >> 5; color4[2] |= (21 * ta + 11 * tb) >> 5; while (nBlocks-- > 0) { blockPtr = rowPtr + pixelPtr; for (pixelY = 0; pixelY < 4; pixelY++) { if (in.getFilePointer() >= in.length()) break; index = in.read(); for (pixelX = 0; pixelX < 4; pixelX++) { idx = (index >> (2 * (3 - pixelX))) & 3; if (blockPtr >= pixels.length) break; pixels[blockPtr] = color4[idx]; final short s = (short) (pixels[blockPtr] & 0x7fff); unpack(s, rtn, blockPtr, pixels.length); blockPtr++; } blockPtr += rowInc; } updateBlock(options.width); } break; case 0x00: blockPtr = rowPtr + pixelPtr; for (pixelY = 0; pixelY < 4; pixelY++) { for (pixelX = 0; pixelX < 4; pixelX++) { if ((pixelY != 0) || (pixelX != 0)) { if (in.getFilePointer() + 2 >= in.length()) break; colorA = in.readShort(); } if (blockPtr >= pixels.length) break; pixels[blockPtr] = colorA; final short s = (short) (pixels[blockPtr] & 0x7fff); unpack(s, rtn, blockPtr, pixels.length); blockPtr++; } blockPtr += rowInc; } updateBlock(options.width); break; } } return rtn; }
/** * The CodecOptions parameter should have the following fields set: {@link * CodecOptions#interleaved interleaved} {@link CodecOptions#littleEndian littleEndian} * * @see Codec#decompress(RandomAccessInputStream, CodecOptions) */ @Override public byte[] decompress(final RandomAccessInputStream in, CodecOptions options) throws FormatException, IOException { BufferedImage b; final long fp = in.getFilePointer(); try { try { while (in.read() != (byte) 0xff || in.read() != (byte) 0xd8) { /* Read to data. */ } in.seek(in.getFilePointer() - 2); } catch (final EOFException e) { in.seek(fp); } b = ImageIO.read(new BufferedInputStream(new DataInputStream(in), 8192)); } catch (final IOException exc) { // probably a lossless JPEG; delegate to LosslessJPEGCodec in.seek(fp); return new LosslessJPEGCodec().decompress(in, options); } if (options == null) options = CodecOptions.getDefaultOptions(); final byte[][] buf = AWTImageTools.getPixelBytes(b, options.littleEndian); // correct for YCbCr encoding, if necessary if (options.ycbcr && buf.length == 3) { final int nBytes = buf[0].length / (b.getWidth() * b.getHeight()); final int mask = (int) (Math.pow(2, nBytes * 8) - 1); for (int i = 0; i < buf[0].length; i += nBytes) { final int y = DataTools.bytesToInt(buf[0], i, nBytes, options.littleEndian); int cb = DataTools.bytesToInt(buf[1], i, nBytes, options.littleEndian); int cr = DataTools.bytesToInt(buf[2], i, nBytes, options.littleEndian); cb = Math.max(0, cb - 128); cr = Math.max(0, cr - 128); final int red = (int) (y + 1.402 * cr) & mask; final int green = (int) (y - 0.34414 * cb - 0.71414 * cr) & mask; final int blue = (int) (y + 1.772 * cb) & mask; DataTools.unpackBytes(red, buf[0], i, nBytes, options.littleEndian); DataTools.unpackBytes(green, buf[1], i, nBytes, options.littleEndian); DataTools.unpackBytes(blue, buf[2], i, nBytes, options.littleEndian); } } byte[] rtn = new byte[buf.length * buf[0].length]; if (buf.length == 1) rtn = buf[0]; else { if (options.interleaved) { int next = 0; for (int i = 0; i < buf[0].length; i++) { for (int j = 0; j < buf.length; j++) { rtn[next++] = buf[j][i]; } } } else { for (int i = 0; i < buf.length; i++) { System.arraycopy(buf[i], 0, rtn, i * buf[0].length, buf[i].length); } } } return rtn; }
@Override public void reset() throws IOException { if (markedPos < 0) throw new IOException("No mark set"); seek(markedPos); }
/** * Reads or skips a string ending with one of the given terminating substrings, using the * specified block size for buffering. * * @param saveString Whether to collect the string from the current file pointer to the * terminating bytes, and return it. If false, returns null. * @param blockSize The block size to use when reading bytes in chunks. * @param terminators The strings for which to search. * @throws IOException If saveString flag is set and the maximum search length (512 MB) is * exceeded. * @return The string from the initial position through the end of the terminating sequence, or * through the end of the stream if no terminating sequence is found, or null if saveString * flag is unset. */ public String findString( final boolean saveString, final int blockSize, final String... terminators) throws IOException { final StringBuilder out = new StringBuilder(); final long startPos = getFilePointer(); long bytesDropped = 0; final long inputLen = length(); long maxLen = inputLen - startPos; final boolean tooLong = saveString && maxLen > MAX_SEARCH_SIZE; if (tooLong) maxLen = MAX_SEARCH_SIZE; boolean match = false; int maxTermLen = 0; for (final String term : terminators) { final int len = term.length(); if (len > maxTermLen) maxTermLen = len; } final InputStreamReader in = new InputStreamReader(this, encoding); final char[] buf = new char[blockSize]; long loc = 0; while (loc < maxLen && getFilePointer() < length() - 1) { // if we're not saving the string, drop any old, unnecessary output if (!saveString) { final int outLen = out.length(); if (outLen >= maxTermLen) { final int dropIndex = outLen - maxTermLen + 1; final String last = out.substring(dropIndex, outLen); out.setLength(0); out.append(last); bytesDropped += dropIndex; } } // read block from stream final int r = in.read(buf, 0, blockSize); if (r <= 0) throw new IOException("Cannot read from stream: " + r); // append block to output out.append(buf, 0, r); // check output, returning smallest possible string int min = Integer.MAX_VALUE, tagLen = 0; for (final String t : terminators) { final int len = t.length(); final int start = (int) (loc - bytesDropped - len); final int value = out.indexOf(t, start < 0 ? 0 : start); if (value >= 0 && value < min) { match = true; min = value; tagLen = len; } } if (match) { // reset stream to proper location seek(startPos + bytesDropped + min + tagLen); // trim output string if (saveString) { out.setLength(min + tagLen); return out.toString(); } return null; } loc += r; } // no match if (tooLong) throw new IOException("Maximum search length reached."); return saveString ? out.toString() : null; }