/// <summary> /// <para> /// Traverses a contiguous sequence of JPEG2000 boxes. /// Visits recursively the contiguous sequence of boxes of /// every superbox. /// </para> /// </summary> /// <param name="stream"></param> /// <param name="boxLimit"></param> /// <returns> /// An ordered collection representing the sequence /// of boxes that appear in the underlying IO stream /// </returns> public static IEnumerable <Jp2Box> TraverseBoxes(Stream stream, long boxLimit) { List <Jp2Box> boxes = new List <Jp2Box>(); long bytesLeft = boxLimit; while (bytesLeft > 0) { Jp2Box box = Jp2Box.Open(stream, bytesLeft); bytesLeft -= box.Length; boxes.Add(box); } return(boxes); }
/// <summary> /// <para> /// Parses a JPEG2000 box from the underlying IO stream. /// </para> /// <para> /// Stream must be positioned at the start of the box /// </para> /// <para> /// Advances the stream to the first byte of the box content /// </para> /// </summary> /// <param name="stream">the underlying IO stream</param> /// <param name="boxLimit"> /// number of bytes that are left to be read in the box container /// </param> /// <returns></returns> public static Jp2Box Open(Stream stream, long boxLimit) { if (boxLimit < MIN_BOX_LENGTH) { throw new ArgumentException( "Not enough data left to parse JPEG2000 box"); } long length = stream.ReadUInt32(); if (length == 0) { // ISO 15443-1, Annex I section 4 Box Definition // If the value of this field is 0, then the length of the // box was not known when the LBox field was written. In this // case, this box contains all bytes up to the end of the file. length = boxLimit; } else if (length > USE_EXTENDED_LENGTH && length < MIN_BOX_LENGTH) { // these values are supposedly reserved for ISO use.. throw new ArgumentException( "JPEG2000 box length cannot be determined"); } uint ucode = stream.ReadUInt32(); uint dataOffset = 8; if (length == USE_EXTENDED_LENGTH) { if (boxLimit < (MIN_BOX_LENGTH + 8)) { throw new ArgumentException( "not enough data to read extended length field"); } ulong ulLength = stream.ReadUInt64(); if (ulLength > long.MaxValue) { throw new ArgumentOutOfRangeException( "box is larger than 2^63 bytes"); } length = (long)ulLength; dataOffset += 8; } if (length > boxLimit) { throw new ArgumentException( "Box length is larger than the limit on box container"); } if (Jp2Box.IsSuperbox(ucode)) { var boxes = TraverseBoxes(stream, length - dataOffset); return(new Jp2Box(ucode, length, dataOffset, boxes)); } else { // advance the stream position beyond the content of this box. // TraverseBoxes recursive call relies on Open call to // advance the stream position. stream.Seek(length - dataOffset, SeekOrigin.Current); return(new Jp2Box(ucode, length, dataOffset)); } }