/** * Get the values of this attribute as multiple short arrays, one per frame. * * <p>Caller needs to supply the number for frames so that pixel data can be split across * per-frame arrays (since not necessarily known when this attribute was created or read. * * <p>This allocates new arrays of sufficient length, which may fail if they are too large, and * defeats the point of leaving the values on disk in the first place. However, it is a fallback * for when the caller does not want to go to the trouble of creating a {@link * java.nio.MappedByteBuffer MappedByteBuffer} from which to get a {@link java.nio.ShortBuffer * ShortBuffer} from the file, or more likely is not even aware that the attribute values have * been left on disk. * * @param numberOfFrames the number of frames * @return the values as an array of arrays of bytes * @throws DicomException thrown if values cannot be read */ public short[][] getShortValuesPerFrame(int numberOfFrames) throws DicomException { // System.err.println("OtherWordAttributeOnDisk.getShortValuesPerFrame(): lazy read of of all // frames into heap allocated memory as per-frame arrays, rather than using memory mapped buffer // :("); short[][] v = null; if (valueLength > 0) { long bytesperframe = valueLength / numberOfFrames; int len = (int) (bytesperframe / 2); v = new short[numberOfFrames][]; try { BinaryInputStream i = new BinaryInputStream( new FileInputStream(file), false /*bigEndian - byte order is irrelevant*/); i.skipInsistently(byteOffset); for (int f = 0; f < numberOfFrames; ++f) { short[] buffer = new short[len]; v[f] = buffer; i.readUnsigned16(buffer, 0, len); } i.close(); } catch (IOException e) { e.printStackTrace(System.err); throw new DicomException( "Failed to read value (length " + valueLength + " dec) in delayed read of " + ValueRepresentation.getAsString(getVR()) + " attribute " + getTag()); } } return v; }
/** * Get the value of this attribute as a short array for one selected frame. * * <p>This allocates a new array of sufficient length, which may fail if it is too large, and * defeats the point of leaving the values on disk in the first place. However, it is a fallback * for when the caller does not want to go to the trouble of creating a {@link * java.nio.MappedByteBuffer MappedByteBuffer} from which to get a {@link java.nio.ShortBuffer * ShortBuffer} from the file, or more likely is not even aware that the attribute values have * been left on disk, because {@link com.pixelmed.dicom.AttributeFactory AttributeFactory} * silently created an instance of this class rather than an in-memory {@link * com.pixelmed.dicom.OtherWordAttribute OtherWordAttribute}. * * @param frameNumber from 0 * @param numberOfFrames the number of frames * @return the values as an array of short * @throws DicomException thrown if values cannot be read */ public short[] getShortValuesForSelectedFrame(int frameNumber, int numberOfFrames) throws DicomException { // System.err.println("OtherWordAttributeOnDisk.getShortValuesForSelectedFrame(): lazy read of // selected frame "+frameNumber+" into heap allocated memory, rather than using memory mapped // buffer :("); short[] buffer = null; long bytesperframe = valueLength / numberOfFrames; long byteoffsetfromstartofattributevalue = bytesperframe * frameNumber; if (byteoffsetfromstartofattributevalue + bytesperframe <= valueLength) { int len = (int) (bytesperframe / 2); buffer = new short[len]; try { BinaryInputStream i = new BinaryInputStream(new FileInputStream(file), bigEndian); i.skipInsistently(byteOffset + byteoffsetfromstartofattributevalue); i.readUnsigned16(buffer, 0, len); i.close(); } catch (IOException e) { throw new DicomException( "Failed to read frame " + frameNumber + " of " + numberOfFrames + " frames, size " + bytesperframe + " dec and offset " + byteoffsetfromstartofattributevalue + " dec bytes in delayed read of " + ValueRepresentation.getAsString(getVR()) + " attribute " + getTag()); } } else { throw new DicomException( "Requested frame " + frameNumber + " of " + numberOfFrames + " frames, size " + bytesperframe + " dec and offset " + byteoffsetfromstartofattributevalue + " dec bytes to read value exceeds length " + valueLength + " dec in delayed read of " + ValueRepresentation.getAsString(getVR()) + " attribute " + getTag()); } return buffer; }
/** * Construct a byte ordered stream from the supplied stream. * * <p>The byte order may be changed later. * * @param i the input stream to read from */ public EncapsulatedInputStream(BinaryInputStream i) { this.i = i; bigEndian = i.isBigEndian(); buffer = new byte[8]; fragment = null; firstTime = true; sequenceDelimiterEncountered = false; endOfFrameEncountered = false; }
/** * Get the values of this attribute as a short array. * * <p>This allocates a new array of sufficient length, which may fail if it is too large, and * defeats the point of leaving the values on disk in the first place. However, it is a fallback * for when the caller does not want to go to the trouble of creating a {@link * java.nio.MappedByteBuffer MappedByteBuffer} from which to get a {@link java.nio.ShortBuffer * ShortBuffer} from the file, or more likely is not even aware that the attribute values have * been left on disk, because {@link com.pixelmed.dicom.AttributeFactory AttributeFactory} * silently created an instance of this class rather than an in-memory {@link * com.pixelmed.dicom.OtherWordAttribute OtherWordAttribute}. * * @return the values as an array of short * @throws DicomException thrown if values cannot be read */ public short[] getShortValues() throws DicomException { // System.err.println("OtherWordAttributeOnDisk.getShortValues(): lazy read into heap allocated // memory, rather than using memory mapped buffer :("); short[] buffer = null; if (valueLength > 0) { int len = (int) (valueLength / 2); buffer = new short[len]; try { BinaryInputStream i = new BinaryInputStream(new FileInputStream(file), bigEndian); i.skipInsistently(byteOffset); i.readUnsigned16(buffer, 0, len); i.close(); } catch (IOException e) { throw new DicomException( "Failed to read value (length " + valueLength + " dec) in delayed read of " + ValueRepresentation.getAsString(getVR()) + " attribute " + getTag()); } } return buffer; }
private long readItemTag() throws IOException { AttributeTag tag = readAttributeTag(); // System.err.println("EncapsulatedInputStream.readItemTag: tag="+tag); long vl = i.readUnsigned32(); // always implicit VR form for items and delimiters bytesRead += 4; if (tag.equals(TagFromName.SequenceDelimitationItem)) { // System.err.println("EncapsulatedInputStream.readItemTag: SequenceDelimitationItem"); vl = 0; // regardless of what was read sequenceDelimiterEncountered = true; } else if (!tag.equals(TagFromName.Item)) { throw new IOException( "Unexpected DICOM tag " + tag + " (vl=" + vl + ") in encapsulated data whilst expecting Item or SequenceDelimitationItem"); } // System.err.println("EncapsulatedInputStream.readItemTag: length="+vl); return vl; }
/** * @param i * @exception IOException */ private AttributeTag readAttributeTag() throws IOException { int group = i.readUnsigned16(); int element = i.readUnsigned16(); bytesRead += 4; return new AttributeTag(group, element); }
/** * Extracts <code>len</code> bytes of data from the current or subsequent fragments. * * @param b the buffer into which the data is read. * @param off the start offset of the data. * @param len the number of bytes read. * @return the total number of bytes read into the buffer (always whatever was asked for), or -1 * if there is no more data because the end of a frame has been reached. * @exception IOException if an I/O error occurs. */ public final int read(byte b[], int off, int len) throws IOException { // System.err.println("EncapsulatedInputStream.read(byte [],"+off+","+len+")"); // System.err.println("EncapsulatedInputStream.read() at start, // fragmentRemaining="+fragmentRemaining); // System.err.println("EncapsulatedInputStream.read() at start, // endOfFrameEncountered="+endOfFrameEncountered); // System.err.println("EncapsulatedInputStream.read() at start, // currentFragmentContainsEndOfFrame="+currentFragmentContainsEndOfFrame); if (endOfFrameEncountered) { // System.err.println("EncapsulatedInputStream.read() returning -1 since // endOfFrameEncountered"); return -1; // i.e., won't advance until nextFrame() is called to reset this state } int count = 0; int remainingToDo = len; while (remainingToDo > 0 && !sequenceDelimiterEncountered && !endOfFrameEncountered) { // System.err.println("EncapsulatedInputStream.read() remainingToDo="+remainingToDo); if (fragment == null) { if (firstTime) { // System.err.println("EncapsulatedInputStream.read() firstTime"); // first time ... skip offset table ... long offsetTableLength = readItemTag(); if (sequenceDelimiterEncountered) { throw new IOException("Expected offset table item tag; got sequence delimiter"); } // System.err.println("EncapsulatedInputStream.read() skipping // offsetTableLength="+offsetTableLength); i.skipInsistently(offsetTableLength); bytesRead += offsetTableLength; firstTime = false; } // load a new fragment ... // System.err.println("EncapsulatedInputStream.read() loading a new fragment"); long vl = readItemTag(); // if sequenceDelimiterEncountered, vl will be zero and no more will be // done if (vl != 0) { currentFragmentContainsEndOfFrame = false; fragmentRemaining = fragmentSize = (int) vl; fragment = new byte[fragmentSize]; i.readInsistently(fragment, 0, fragmentSize); bytesRead += fragmentSize; fragmentOffset = 0; // System.err.println("EncapsulatedInputStream.read() fragmentRemaining // initially="+fragmentRemaining); // System.err.println("EncapsulatedInputStream.read() fragment = // "+com.pixelmed.utils.HexDump.dump(fragment)); // Ignore everything between (the last) EOI marker and the end of the fragment int positionOfEOI = fragmentRemaining - 1; while (--positionOfEOI > 0) { int firstMarkerByte = fragment[positionOfEOI] & 0xff; int secondMarkerByte = fragment[positionOfEOI + 1] & 0xff; // System.err.println("EncapsulatedInputStream.read() fragment // fragment["+positionOfEOI+"] = 0x"+Integer.toHexString(firstMarkerByte)); // System.err.println("EncapsulatedInputStream.read() fragment // fragment["+(positionOfEOI+1)+"] = 0x"+Integer.toHexString(secondMarkerByte)); if (firstMarkerByte == 0xff && secondMarkerByte == 0xd9) { currentFragmentContainsEndOfFrame = true; break; } } // System.err.println("EncapsulatedInputStream.read() positionOfEOI="+positionOfEOI); if (positionOfEOI > 0) { // will be zero if we did not find one fragmentRemaining = positionOfEOI + 2; // effectively skips all (hopefully padding) bytes after the EOI } // System.err.println("EncapsulatedInputStream.read() fragmentRemaining after removing // trailing padding="+fragmentRemaining); } } int amountToCopyFromThisFragment = remainingToDo < fragmentRemaining ? remainingToDo : fragmentRemaining; // System.err.println("EncapsulatedInputStream.read() // amountToCopyFromThisFragment="+amountToCopyFromThisFragment); if (amountToCopyFromThisFragment > 0) { System.arraycopy(fragment, fragmentOffset, b, off, amountToCopyFromThisFragment); off += amountToCopyFromThisFragment; fragmentOffset += amountToCopyFromThisFragment; fragmentRemaining -= amountToCopyFromThisFragment; remainingToDo -= amountToCopyFromThisFragment; count += amountToCopyFromThisFragment; } if (fragmentRemaining <= 0) { fragment = null; if (currentFragmentContainsEndOfFrame) { endOfFrameEncountered = true; // once EOI has been seen in a fragment, use the rest of the fragment including // the EOI, but no further } } } // System.err.println("EncapsulatedInputStream.read() returning count="+count); // System.err.println("EncapsulatedInputStream.read() // returning="+com.pixelmed.utils.HexDump.dump(fragment,off,count)); return count == 0 ? -1 : count; // always returns more than 0 unless end, which is signalled by -1 }